/src/trafficserver/include/proxy/hdrs/HdrHeap.h
Line | Count | Source |
1 | | /** @file |
2 | | |
3 | | A brief file description |
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 | | /**************************************************************************** |
25 | | |
26 | | HdrHeap.h |
27 | | |
28 | | Description: |
29 | | |
30 | | |
31 | | ****************************************************************************/ |
32 | | |
33 | | #pragma once |
34 | | |
35 | | #include "tscore/Ptr.h" |
36 | | #include "tscore/ink_assert.h" |
37 | | #include "swoc/Scalar.h" |
38 | | #include "proxy/hdrs/HdrToken.h" |
39 | | |
40 | | // Objects in the heap must currently be aligned to 8 byte boundaries, |
41 | | // so their (address & HDR_PTR_ALIGNMENT_MASK) == 0 |
42 | | |
43 | | static constexpr size_t HDR_PTR_SIZE = sizeof(uint64_t); |
44 | | static constexpr size_t HDR_PTR_ALIGNMENT_MASK = HDR_PTR_SIZE - 1L; |
45 | | using HdrHeapMarshalBlocks = swoc::Scalar<HDR_PTR_SIZE>; |
46 | | |
47 | | // A many of the operations regarding read-only str |
48 | | // heaps are hand unrolled in the code. Changing |
49 | | // this value requires a full pass through HdrBuf.cc |
50 | | // to fix the unrolled operations |
51 | | static constexpr unsigned HDR_BUF_RONLY_HEAPS = 3; |
52 | | |
53 | | class IOBufferBlock; |
54 | | |
55 | | enum class HdrHeapObjType : uint8_t { |
56 | | EMPTY = 0, |
57 | | RAW = 1, |
58 | | URL = 2, |
59 | | HTTP_HEADER = 3, |
60 | | MIME_HEADER = 4, |
61 | | FIELD_BLOCK = 5, |
62 | | FIELD_STANDALONE = 6, // not a type that lives in HdrHeaps |
63 | | FIELD_SDK_HANDLE = 7, // not a type that lives in HdrHeaps |
64 | | }; |
65 | | |
66 | | struct HdrHeapObjImpl { |
67 | | uint32_t m_type : 8; |
68 | | uint32_t m_length : 20; |
69 | | uint32_t m_obj_flags : 4; |
70 | | }; |
71 | | |
72 | | /*------------------------------------------------------------------------- |
73 | | -------------------------------------------------------------------------*/ |
74 | | |
75 | | extern void obj_describe(HdrHeapObjImpl *obj, bool recurse); |
76 | | |
77 | | inline int |
78 | | obj_is_aligned(HdrHeapObjImpl *obj) |
79 | 0 | { |
80 | 0 | return (((((uintptr_t)obj) & HDR_PTR_ALIGNMENT_MASK) == 0) && ((obj->m_length & HDR_PTR_ALIGNMENT_MASK) == 0)); |
81 | 0 | } |
82 | | |
83 | | inline void |
84 | | obj_clear_data(HdrHeapObjImpl *obj) |
85 | 0 | { |
86 | 0 | char *ptr = (char *)obj; |
87 | 0 | int hdr_length = sizeof(HdrHeapObjImpl); |
88 | 0 | memset(ptr + hdr_length, '\0', obj->m_length - hdr_length); |
89 | 0 | } |
90 | | |
91 | | inline void |
92 | | obj_copy_data(HdrHeapObjImpl *s_obj, HdrHeapObjImpl *d_obj) |
93 | 0 | { |
94 | 0 | char *src, *dst; |
95 | |
|
96 | 0 | ink_assert((s_obj->m_length == d_obj->m_length) && (s_obj->m_type == d_obj->m_type)); |
97 | |
|
98 | 0 | int hdr_length = sizeof(HdrHeapObjImpl); |
99 | 0 | src = (char *)s_obj + hdr_length; |
100 | 0 | dst = (char *)d_obj + hdr_length; |
101 | 0 | memcpy(dst, src, d_obj->m_length - hdr_length); |
102 | 0 | } |
103 | | |
104 | | inline void |
105 | | obj_copy(HdrHeapObjImpl *s_obj, char *d_addr) |
106 | 0 | { |
107 | 0 | memcpy(d_addr, (char *)s_obj, s_obj->m_length); |
108 | 0 | } |
109 | | |
110 | | inline void |
111 | | obj_init_header(HdrHeapObjImpl *obj, HdrHeapObjType type, uint32_t nbytes, uint32_t obj_flags) |
112 | 0 | { |
113 | 0 | obj->m_type = static_cast<uint32_t>(type); |
114 | 0 | obj->m_length = nbytes; |
115 | 0 | obj->m_obj_flags = obj_flags; |
116 | 0 | } |
117 | | |
118 | | /*------------------------------------------------------------------------- |
119 | | -------------------------------------------------------------------------*/ |
120 | | |
121 | | enum class HdrBufMagic : uint32_t { ALIVE = 0xabcdfeed, MARSHALED = 0xdcbafeed, DEAD = 0xabcddead, CORRUPT = 0xbadbadcc }; |
122 | | |
123 | | class HdrStrHeap : public RefCountObj |
124 | | { |
125 | | public: |
126 | | static constexpr int DEFAULT_SIZE = 2048; |
127 | | |
128 | | void free() override; |
129 | | |
130 | | char *allocate(int nbytes); |
131 | | char *expand(char *ptr, int old_size, int new_size); |
132 | | uint32_t |
133 | | space_avail() const |
134 | 0 | { |
135 | 0 | return _avail_size; |
136 | 0 | } |
137 | | uint32_t |
138 | | total_size() const |
139 | 0 | { |
140 | 0 | return _total_size; |
141 | 0 | } |
142 | | |
143 | | bool contains(const char *str) const; |
144 | | |
145 | | static HdrStrHeap *alloc(int heap_size); |
146 | | |
147 | | private: |
148 | 0 | HdrStrHeap(uint32_t total_size) : _total_size{total_size} {} |
149 | | |
150 | | uint32_t const _total_size; |
151 | | uint32_t _avail_size; |
152 | | }; |
153 | | |
154 | | inline bool |
155 | | HdrStrHeap::contains(const char *str) const |
156 | 0 | { |
157 | 0 | return reinterpret_cast<char const *>(this + 1) <= str && str < reinterpret_cast<char const *>(this) + _total_size; |
158 | 0 | } |
159 | | |
160 | | struct StrHeapDesc { |
161 | | StrHeapDesc() = default; |
162 | | |
163 | | Ptr<RefCountObj> m_ref_count_ptr; |
164 | | char const *m_heap_start = nullptr; |
165 | | int32_t m_heap_len = 0; |
166 | | bool m_locked = false; |
167 | | |
168 | | bool |
169 | | contains(const char *str) const |
170 | 0 | { |
171 | 0 | return (str >= m_heap_start && str < (m_heap_start + m_heap_len)); |
172 | 0 | } |
173 | | }; |
174 | | |
175 | | class HdrHeap |
176 | | { |
177 | | public: |
178 | | static constexpr int DEFAULT_SIZE = 2048; |
179 | | |
180 | | void init(); |
181 | | void destroy(); |
182 | | |
183 | | // PtrHeap allocation |
184 | | HdrHeapObjImpl *allocate_obj(int nbytes, HdrHeapObjType type); |
185 | | void deallocate_obj(HdrHeapObjImpl *obj); |
186 | | |
187 | | // StrHeap allocation |
188 | | char *allocate_str(int nbytes); |
189 | | char *expand_str(const char *old_str, int old_len, int new_len); |
190 | | char *duplicate_str(const char *str, int nbytes); |
191 | | void free_string(const char *s, int len); |
192 | | |
193 | | // Marshalling |
194 | | int marshal_length(); |
195 | | int marshal(char *buf, int length); |
196 | | int unmarshal(int buf_length, int obj_type, HdrHeapObjImpl **found_obj, RefCountObj *block_ref); |
197 | | /// Computes the valid data size of an unmarshalled instance. |
198 | | /// Callers should round up to HDR_PTR_SIZE to get the actual footprint. |
199 | | int unmarshal_size() const; // TBD - change this name, it's confusing. |
200 | | // One option - overload marshal_length to return this value if @a magic is HdrBufMagic::MARSHALED. |
201 | | |
202 | | void inherit_string_heaps(const HdrHeap *inherit_from); |
203 | | int attach_block(IOBufferBlock *b, const char *use_start); |
204 | | void set_ronly_str_heap_end(int slot, const char *end); |
205 | | |
206 | | // Lock read only str heaps so that can't be moved around |
207 | | // by a heap consolidation. Does NOT lock for Multi-Threaded |
208 | | // access! |
209 | | void |
210 | | lock_ronly_str_heap(unsigned i) |
211 | 0 | { |
212 | 0 | m_ronly_heap[i].m_locked = true; |
213 | 0 | } |
214 | | |
215 | | void |
216 | | unlock_ronly_str_heap(unsigned i) |
217 | 0 | { |
218 | 0 | m_ronly_heap[i].m_locked = false; |
219 | 0 | // INKqa11238 |
220 | 0 | // Move slot i to the first available slot in m_ronly_heap[]. |
221 | 0 | // The move is necessary because the rest of the code assumes |
222 | 0 | // heaps are always allocated in order. |
223 | 0 | for (unsigned j = 0; j < i; j++) { |
224 | 0 | if (m_ronly_heap[j].m_heap_start == nullptr) { |
225 | 0 | // move slot i to slot j |
226 | 0 | m_ronly_heap[j].m_ref_count_ptr = m_ronly_heap[i].m_ref_count_ptr; |
227 | 0 | m_ronly_heap[j].m_heap_start = m_ronly_heap[i].m_heap_start; |
228 | 0 | m_ronly_heap[j].m_heap_len = m_ronly_heap[i].m_heap_len; |
229 | 0 | m_ronly_heap[j].m_locked = m_ronly_heap[i].m_locked; |
230 | 0 | m_ronly_heap[i].m_ref_count_ptr = nullptr; |
231 | 0 | m_ronly_heap[i].m_heap_start = nullptr; |
232 | 0 | m_ronly_heap[i].m_heap_len = 0; |
233 | 0 | m_ronly_heap[i].m_locked = false; |
234 | 0 | break; // Did the move, time to go. |
235 | 0 | } |
236 | 0 | } |
237 | 0 | } |
238 | | |
239 | | // Working function to copy strings into a new heap |
240 | | // Unlike the HDR_MOVE_STR macro, this function will call |
241 | | // allocate_str which will update the new_heap to create more space |
242 | | // if there is not originally sufficient space |
243 | | inline std::string_view |
244 | | localize(const std::string_view &string) |
245 | 0 | { |
246 | 0 | auto length = string.length(); |
247 | 0 | if (length > 0) { |
248 | 0 | char *new_str = this->allocate_str(length); |
249 | 0 | if (new_str) { |
250 | 0 | memcpy(new_str, string.data(), length); |
251 | 0 | } else { |
252 | 0 | length = 0; |
253 | 0 | } |
254 | 0 | return {new_str, length}; |
255 | 0 | } |
256 | 0 | return {nullptr, 0}; |
257 | 0 | } |
258 | | |
259 | | // Sanity Check Functions |
260 | | void sanity_check_strs(); |
261 | | bool check_marshalled(uint32_t buf_length); |
262 | | |
263 | | // Debugging functions |
264 | | void dump_heap(int len = -1); |
265 | | |
266 | | HdrBufMagic m_magic; |
267 | | char *m_free_start; |
268 | | char *m_data_start; |
269 | | uint32_t m_size; |
270 | | |
271 | | bool m_writeable; |
272 | | |
273 | | // Overflow block ptr |
274 | | // Overflow blocks are necessary because we can |
275 | | // run out of space in the header heap and the |
276 | | // heap is not rellocatable |
277 | | // Overflow blocks have the HdrHeap full structure |
278 | | // header on them, although only first block can |
279 | | // point to string heaps |
280 | | HdrHeap *m_next; |
281 | | |
282 | | // HdrBuf heap pointers |
283 | | uint32_t m_free_size; |
284 | | |
285 | | int demote_rw_str_heap(); |
286 | | void coalesce_str_heaps(int incoming_size = 0); |
287 | | void evacuate_from_str_heaps(HdrStrHeap *new_heap); |
288 | | size_t required_space_for_evacuation(); |
289 | | bool attach_str_heap(char const *h_start, int h_len, RefCountObj *h_ref_obj, int *index); |
290 | | |
291 | | uint64_t total_used_size() const; |
292 | | |
293 | | /** Struct to prevent garbage collection on heaps. |
294 | | This bumps the reference count to the heap containing the pointer |
295 | | while the instance of this class exists. When it goes out of scope |
296 | | the reference is dropped. This is useful inside a method or block |
297 | | to keep the required heap data around until leaving the scope. |
298 | | */ |
299 | | class HeapGuard |
300 | | { |
301 | | public: |
302 | | /// Construct the protection. |
303 | | HeapGuard(HdrHeap *heap, const char *str) |
304 | 0 | { |
305 | 0 | if (heap->m_read_write_heap && heap->m_read_write_heap->contains(str)) { |
306 | 0 | _ptr = heap->m_read_write_heap.get(); |
307 | 0 | } else { |
308 | 0 | for (auto &i : heap->m_ronly_heap) { |
309 | 0 | if (i.contains(str)) { |
310 | 0 | _ptr = i.m_ref_count_ptr; |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | } |
314 | 0 | } |
315 | 0 | } |
316 | | |
317 | | // There's no need to have a destructor here, the default dtor will take care of |
318 | | // releasing the (potentially) locked heap. |
319 | | |
320 | | private: |
321 | | /// The heap we protect (if any) |
322 | | Ptr<RefCountObj> _ptr; |
323 | | }; |
324 | | |
325 | | // String Heap access |
326 | | Ptr<HdrStrHeap> m_read_write_heap; |
327 | | StrHeapDesc m_ronly_heap[HDR_BUF_RONLY_HEAPS]; |
328 | | int m_lost_string_space; |
329 | | }; |
330 | | |
331 | | static constexpr HdrHeapMarshalBlocks HDR_HEAP_HDR_SIZE{swoc::round_up(sizeof(HdrHeap))}; |
332 | | static constexpr size_t HDR_MAX_ALLOC_SIZE = HdrHeap::DEFAULT_SIZE - HDR_HEAP_HDR_SIZE; |
333 | | |
334 | | inline void |
335 | | HdrHeap::free_string(const char *s, int len) |
336 | 0 | { |
337 | 0 | if (s && len > 0) { |
338 | 0 | m_lost_string_space += len; |
339 | 0 | } |
340 | 0 | } |
341 | | |
342 | | inline int |
343 | | HdrHeap::unmarshal_size() const |
344 | 0 | { |
345 | 0 | return m_size + m_ronly_heap[0].m_heap_len; |
346 | 0 | } |
347 | | |
348 | | // |
349 | | struct MarshalXlate { |
350 | | char const *start = nullptr; |
351 | | char const *end = nullptr; |
352 | | char const *offset = nullptr; |
353 | 0 | MarshalXlate() {} |
354 | | }; |
355 | | |
356 | | struct HeapCheck { |
357 | | char const *start; |
358 | | char const *end; |
359 | | }; |
360 | | |
361 | | // Nasty macro to do string marshalling |
362 | | #define HDR_MARSHAL_STR(ptr, table, nentries) \ |
363 | 0 | if (ptr) { \ |
364 | 0 | int found = 0; \ |
365 | 0 | for (int i = 0; i < nentries; i++) { \ |
366 | 0 | if (ptr >= table[i].start && ptr <= table[i].end) { \ |
367 | 0 | ptr = (((char *)ptr) - (uintptr_t)table[i].offset); \ |
368 | 0 | found = 1; \ |
369 | 0 | break; \ |
370 | 0 | } \ |
371 | 0 | } \ |
372 | 0 | ink_assert(found); \ |
373 | 0 | if (found == 0) { \ |
374 | 0 | return -1; \ |
375 | 0 | } \ |
376 | 0 | } |
377 | | |
378 | | // Nasty macro to do string marshalling |
379 | | #define HDR_MARSHAL_STR_1(ptr, table) \ |
380 | 0 | if (ptr) { \ |
381 | 0 | int found = 0; \ |
382 | 0 | if (ptr >= table[0].start && ptr <= table[0].end) { \ |
383 | 0 | ptr = (((char *)ptr) - (uintptr_t)table[0].offset); \ |
384 | 0 | found = 1; \ |
385 | 0 | } \ |
386 | 0 | ink_assert(found); \ |
387 | 0 | if (found == 0) { \ |
388 | 0 | return -1; \ |
389 | 0 | } \ |
390 | 0 | } |
391 | | |
392 | | #define HDR_MARSHAL_PTR(ptr, type, table, nentries) \ |
393 | 0 | if (ptr) { \ |
394 | 0 | int found = 0; \ |
395 | 0 | for (int i = 0; i < nentries; i++) { \ |
396 | 0 | if ((char *)ptr >= table[i].start && (char *)ptr <= table[i].end) { \ |
397 | 0 | ptr = (type *)(((char *)ptr) - (uintptr_t)table[i].offset); \ |
398 | 0 | found = 1; \ |
399 | 0 | break; \ |
400 | 0 | } \ |
401 | 0 | } \ |
402 | 0 | ink_assert(found); \ |
403 | 0 | if (found == 0) { \ |
404 | 0 | return -1; \ |
405 | 0 | } \ |
406 | 0 | } |
407 | | |
408 | | #define HDR_MARSHAL_PTR_1(ptr, type, table) \ |
409 | 0 | if (ptr) { \ |
410 | 0 | int found = 0; \ |
411 | 0 | if ((char *)ptr >= table[0].start && (char *)ptr <= table[0].end) { \ |
412 | 0 | ptr = (type *)(((char *)ptr) - (uintptr_t)table[0].offset); \ |
413 | 0 | found = 1; \ |
414 | 0 | } \ |
415 | 0 | ink_assert(found); \ |
416 | 0 | if (found == 0) { \ |
417 | 0 | return -1; \ |
418 | 0 | } \ |
419 | 0 | } |
420 | | |
421 | | #define HDR_UNMARSHAL_STR(ptr, offset) \ |
422 | 0 | if (ptr) { \ |
423 | 0 | ptr = ((char *)ptr) + offset; \ |
424 | 0 | } |
425 | | |
426 | | #define HDR_UNMARSHAL_PTR(ptr, type, offset) \ |
427 | 0 | if (ptr) { \ |
428 | 0 | ptr = (type *)(((char *)ptr) + offset); \ |
429 | 0 | } |
430 | | |
431 | | // Nasty macro to do string evacuation. Assumes |
432 | | // new heap = new_heap |
433 | | #define HDR_MOVE_STR(str, len) \ |
434 | 0 | { \ |
435 | 0 | if (str) { \ |
436 | 0 | char *new_str = new_heap->allocate(len); \ |
437 | 0 | if (new_str) \ |
438 | 0 | memcpy(new_str, str, len); \ |
439 | 0 | str = new_str; \ |
440 | 0 | } \ |
441 | 0 | } |
442 | | |
443 | | // Nasty macro to do verify all strings it |
444 | | // in attached heaps |
445 | | #define CHECK_STR(str, len, _heaps, _num_heaps) \ |
446 | 0 | { \ |
447 | 0 | if (str) { \ |
448 | 0 | int found = 0; \ |
449 | 0 | for (int i = 0; i < _num_heaps; i++) { \ |
450 | 0 | if (str >= _heaps[i].start && str + len <= heaps[i].end) { \ |
451 | 0 | found = 1; \ |
452 | 0 | } \ |
453 | 0 | } \ |
454 | 0 | ink_release_assert(found); \ |
455 | 0 | } \ |
456 | 0 | } |
457 | | |
458 | | // struct HdrHeapSDKHandle() |
459 | | // |
460 | | // Handle to a HdrHeap. |
461 | | // |
462 | | // Intended to be subclassed and contain a |
463 | | // object pointer that points into the heap |
464 | | // |
465 | | struct HdrHeapSDKHandle { |
466 | | public: |
467 | 0 | HdrHeapSDKHandle() {} |
468 | 0 | ~HdrHeapSDKHandle() { clear(); } |
469 | | // clear() only deallocates chained SDK return values |
470 | | // The underlying MBuffer is left untouched |
471 | | void clear(); |
472 | | |
473 | | // destroy() frees the underlying MBuffer and deallocates all chained |
474 | | // SDK return values |
475 | | void destroy(); |
476 | | |
477 | | void set(const HdrHeapSDKHandle *from); |
478 | | const char *make_sdk_string(const char *raw_str, int raw_str_len); |
479 | | |
480 | | HdrHeap *m_heap = nullptr; |
481 | | |
482 | | // In order to prevent gratitous refcounting, |
483 | | // automatic C++ copies are disabled! |
484 | | HdrHeapSDKHandle(const HdrHeapSDKHandle &r) = delete; |
485 | | HdrHeapSDKHandle &operator=(const HdrHeapSDKHandle &r) = delete; |
486 | | }; |
487 | | |
488 | | inline void |
489 | | HdrHeapSDKHandle::destroy() |
490 | 0 | { |
491 | 0 | if (m_heap) { |
492 | 0 | m_heap->destroy(); |
493 | 0 | } |
494 | 0 | clear(); |
495 | 0 | } |
496 | | |
497 | | inline void |
498 | | HdrHeapSDKHandle::clear() |
499 | 0 | { |
500 | 0 | m_heap = nullptr; |
501 | 0 | } |
502 | | |
503 | | inline void |
504 | | HdrHeapSDKHandle::set(const HdrHeapSDKHandle *from) |
505 | 0 | { |
506 | 0 | clear(); |
507 | 0 | m_heap = from->m_heap; |
508 | 0 | } |
509 | | |
510 | | HdrHeap *new_HdrHeap(int size = HdrHeap::DEFAULT_SIZE); |
511 | | |
512 | | void hdr_heap_test(); |