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