1 /** @file
2 
3   This file implements the LogBuffer class, a thread-safe buffer for
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   @section description
24   This file implements the LogBuffer class, a thread-safe buffer for
25   recording log entries. See the header file LogBuffer.h for more
26   information on the structure of a LogBuffer.
27  */
28 #include "tscore/ink_platform.h"
29 #include "tscore/BufferWriter.h"
30 
31 #include <cstdio>
32 #include <cstdlib>
33 #include <cstring>
34 
35 #include "P_EventSystem.h"
36 #include "LogField.h"
37 #include "LogFilter.h"
38 #include "LogFormat.h"
39 #include "LogUtils.h"
40 #include "LogFile.h"
41 #include "LogObject.h"
42 #include "LogAccess.h"
43 #include "LogConfig.h"
44 #include "LogBuffer.h"
45 #include "Log.h"
46 
47 struct FieldListCacheElement {
48   LogFieldList *fieldlist;
49   char *symbol_str;
50 };
51 
52 enum {
53   FIELDLIST_CACHE_SIZE = 256,
54 };
55 
56 FieldListCacheElement fieldlist_cache[FIELDLIST_CACHE_SIZE];
57 int fieldlist_cache_entries = 0;
58 int32_t LogBuffer::M_ID;
59 
60 /*-------------------------------------------------------------------------
61   The following LogBufferHeader routines are used to grab strings out from
62   the data section using the offsets held in the buffer header.
63   -------------------------------------------------------------------------*/
64 
65 char *
fmt_fieldlist()66 LogBufferHeader::fmt_fieldlist()
67 {
68   char *addr = nullptr;
69   if (fmt_fieldlist_offset) {
70     addr = reinterpret_cast<char *>(this) + fmt_fieldlist_offset;
71   }
72   return addr;
73 }
74 
75 char *
fmt_printf()76 LogBufferHeader::fmt_printf()
77 {
78   char *addr = nullptr;
79   if (fmt_printf_offset) {
80     addr = reinterpret_cast<char *>(this) + fmt_printf_offset;
81   }
82   return addr;
83 }
84 
85 char *
src_hostname()86 LogBufferHeader::src_hostname()
87 {
88   char *addr = nullptr;
89   if (src_hostname_offset) {
90     addr = reinterpret_cast<char *>(this) + src_hostname_offset;
91   }
92   return addr;
93 }
94 
95 char *
log_filename()96 LogBufferHeader::log_filename()
97 {
98   char *addr = nullptr;
99   if (log_filename_offset) {
100     addr = reinterpret_cast<char *>(this) + log_filename_offset;
101   }
102   return addr;
103 }
104 
LogBuffer(const LogConfig * cfg,LogObject * owner,size_t size,size_t buf_align,size_t write_align)105 LogBuffer::LogBuffer(const LogConfig *cfg, LogObject *owner, size_t size, size_t buf_align, size_t write_align)
106   : m_size(size), m_buf_align(buf_align), m_write_align(write_align), m_owner(owner), m_references(0)
107 {
108   size_t hdr_size;
109 
110   // create the buffer
111   //
112   int64_t alloc_size = size + buf_align;
113 
114   if (alloc_size <= BUFFER_SIZE_FOR_INDEX(cfg->logbuffer_max_iobuf_index)) {
115     m_buffer_fast_allocator_size = buffer_size_to_index(alloc_size, cfg->logbuffer_max_iobuf_index);
116     m_unaligned_buffer           = static_cast<char *>(ioBufAllocator[m_buffer_fast_allocator_size].alloc_void());
117   } else {
118     m_buffer_fast_allocator_size = -1;
119     m_unaligned_buffer           = static_cast<char *>(ats_malloc(alloc_size));
120   }
121   m_buffer = static_cast<char *>(align_pointer_forward(m_unaligned_buffer, buf_align));
122 
123   // add the header
124   hdr_size = _add_buffer_header(cfg);
125 
126   // initialize buffer state
127   m_state.s.offset = hdr_size;
128 
129   // update the buffer id (m_id gets the old value)
130   m_id = static_cast<uint32_t>(ink_atomic_increment(&M_ID, 1));
131 
132   m_expiration_time = LogUtils::timestamp() + cfg->max_secs_per_buffer;
133 
134   Debug("log-logbuffer", "[%p] Created buffer %u for %s at address %p, size %d", this_ethread(), m_id, m_owner->get_base_filename(),
135         m_buffer, (int)size);
136 }
137 
LogBuffer(LogObject * owner,LogBufferHeader * header)138 LogBuffer::LogBuffer(LogObject *owner, LogBufferHeader *header)
139   : m_unaligned_buffer(nullptr),
140     m_buffer(reinterpret_cast<char *>(header)),
141     m_size(0),
142     m_buf_align(LB_DEFAULT_ALIGN),
143     m_write_align(INK_MIN_ALIGN),
144     m_buffer_fast_allocator_size(-1),
145     m_expiration_time(0),
146     m_owner(owner),
147     m_header(header),
148     m_references(0)
149 {
150   // This constructor does not allocate a buffer because it gets it as
151   // an argument. We set m_unaligned_buffer to NULL, which means that
152   // no checkout writes or checkin writes are allowed. This is enforced
153   // by the asserts in checkout_write and checkin_write
154 
155   // update the buffer id (m_id gets the old value)
156   //
157   m_id = static_cast<uint32_t>(ink_atomic_increment(&M_ID, 1));
158 
159   Debug("log-logbuffer", "[%p] Created repurposed buffer %u for %s at address %p", this_ethread(), m_id,
160         m_owner->get_base_filename(), m_buffer);
161 }
162 
163 void
freeLogBuffer()164 LogBuffer::freeLogBuffer()
165 {
166   char *log_buffer = nullptr;
167 
168   if (m_unaligned_buffer) {
169     log_buffer = m_unaligned_buffer;
170   } else {
171     log_buffer = m_buffer;
172   }
173   if (log_buffer) {
174     Debug("log-logbuffer", "[%p] Deleting buffer %u at address %p", this_ethread(), m_id, log_buffer);
175     if (m_buffer_fast_allocator_size >= 0) {
176       ioBufAllocator[m_buffer_fast_allocator_size].free_void(log_buffer);
177     } else {
178       ats_free(log_buffer);
179     }
180   }
181 }
182 
~LogBuffer()183 LogBuffer::~LogBuffer()
184 {
185   freeLogBuffer();
186   m_buffer           = nullptr;
187   m_unaligned_buffer = nullptr;
188 }
189 
190 /*-------------------------------------------------------------------------
191   LogBuffer::checkout_write
192   -------------------------------------------------------------------------*/
193 
194 LogBuffer::LB_ResultCode
checkout_write(size_t * write_offset,size_t write_size)195 LogBuffer::checkout_write(size_t *write_offset, size_t write_size)
196 {
197   // checkout_write should not be called if m_unaligned_buffer was
198   // not allocated, which means that the actual buffer data was given
199   // to the buffer upon construction. Or, in other words, that
200   // LogBuffer::LogBuffer(LogObject *owner, LogBufferHeader *header)
201   // was used to construct the object
202   //
203   ink_assert(m_unaligned_buffer);
204 
205   LB_ResultCode ret_val = LB_BUSY;
206   LB_State old_s, new_s;
207   size_t offset            = 0;
208   size_t actual_write_size = INK_ALIGN(write_size + sizeof(LogEntryHeader), m_write_align);
209 
210   uint64_t retries = static_cast<uint64_t>(-1);
211   do {
212     // we want sequence points between these two statements
213     old_s = m_state;
214     new_s = old_s;
215 
216     if (old_s.s.full) {
217       // the buffer has already been set to full by somebody else
218       // just tell the caller to retry
219       ret_val = LB_RETRY;
220       break;
221     } else {
222       // determine what the state would be if nobody changes it
223       // before we do
224 
225       if (write_offset) {
226         if (old_s.s.offset + actual_write_size <= m_size) {
227           // there is room for this entry, update the state
228 
229           offset = old_s.s.offset;
230 
231           ++new_s.s.num_writers;
232           new_s.s.offset += actual_write_size;
233           ++new_s.s.num_entries;
234 
235           ret_val = LB_OK;
236         } else {
237           // there is no room for this entry
238 
239           if (old_s.s.num_entries == 0) {
240             ret_val = LB_BUFFER_TOO_SMALL;
241           } else {
242             new_s.s.full = 1;
243             ret_val      = old_s.s.num_writers ? LB_FULL_ACTIVE_WRITERS : LB_FULL_NO_WRITERS;
244           }
245         }
246       } else {
247         // this is a request to set the buffer as full
248         // (write_offset == NULL)
249 
250         if (old_s.s.num_entries) {
251           new_s.s.full = 1;
252 
253           ret_val = (old_s.s.num_writers ? LB_FULL_ACTIVE_WRITERS : LB_FULL_NO_WRITERS);
254         } else {
255           // the buffer has no entries, do nothing
256 
257           ret_val = LB_OK;
258           break;
259         }
260       }
261 
262       if (switch_state(old_s, new_s)) {
263         // we succeeded in setting the new state
264         break;
265       }
266     }
267     ret_val = LB_BUSY;
268   } while (--retries);
269 
270   // add the entry header to the buffer if this was a real checkout and
271   // the checkout was successful
272   //
273   if (write_offset && ret_val == LB_OK) {
274     // disable statistics for now
275     // ProxyMutex *mutex = this_ethread()->mutex;
276     // ink_release_assert(mutex->thread_holding == this_ethread());
277     // SUM_DYN_STAT(log_stat_bytes_buffered_stat, actual_write_size);
278 
279     LogEntryHeader *entry_header = reinterpret_cast<LogEntryHeader *>(&m_buffer[offset]);
280     // entry_header->timestamp = LogUtils::timestamp();
281     struct timeval tp = ink_gettimeofday();
282 
283     entry_header->timestamp      = tp.tv_sec;
284     entry_header->timestamp_usec = tp.tv_usec;
285     entry_header->entry_len      = actual_write_size;
286 
287     *write_offset = offset + sizeof(LogEntryHeader);
288   }
289   //    Debug("log-logbuffer","[%p] %s for buffer %u (%s) returning %d",
290   //        this_ethread(),
291   //        (write_offset ? "checkout_write" : "force_new_buffer"),
292   //        m_id, m_owner->get_base_filename(), ret_val);
293 
294   return ret_val;
295 }
296 
297 /*-------------------------------------------------------------------------
298   LogBuffer::checkin_write
299   -------------------------------------------------------------------------*/
300 
301 LogBuffer::LB_ResultCode
checkin_write(size_t write_offset)302 LogBuffer::checkin_write(size_t write_offset)
303 {
304   // checkin_write should not be called if m_unaligned_buffer was
305   // not allocated, which means that the actual buffer data was given
306   // to the buffer upon construction. Or, in other words, that
307   // LogBuffer::LogBuffer(LogObject *owner, LogBufferHeader *header)
308   // was used to construct the object
309   //
310   ink_assert(m_unaligned_buffer);
311 
312   LB_ResultCode ret_val = LB_OK;
313   LB_State old_s, new_s;
314 
315   do {
316     new_s = old_s = m_state;
317 
318     ink_assert(write_offset < old_s.s.offset);
319     ink_assert(old_s.s.num_writers > 0);
320 
321     if (--new_s.s.num_writers == 0) {
322       ret_val = (old_s.s.full ? LB_ALL_WRITERS_DONE : LB_OK);
323     }
324 
325   } while (!switch_state(old_s, new_s));
326 
327   //    Debug("log-logbuffer","[%p] checkin_write for buffer %u (%s) "
328   //        "returning %d (%u writers left)", this_ethread(),
329   //        m_id, m_owner->get_base_filename(), ret_val, writers_left);
330 
331   return ret_val;
332 }
333 
334 unsigned
add_header_str(const char * str,char * buf_ptr,unsigned buf_len)335 LogBuffer::add_header_str(const char *str, char *buf_ptr, unsigned buf_len)
336 {
337   unsigned len = 0;
338   // This was ambiguous - should it be the real strlen or the
339   // rounded up value? Given the +1, presumably it's the real length
340   // plus the terminating nul.
341   if (likely(str && (len = (unsigned)(::strlen(str) + 1)) < buf_len)) {
342     ink_strlcpy(buf_ptr, str, buf_len);
343   }
344   return len;
345 }
346 
347 size_t
_add_buffer_header(const LogConfig * cfg)348 LogBuffer::_add_buffer_header(const LogConfig *cfg)
349 {
350   size_t header_len;
351 
352   //
353   // initialize the header
354   //
355   LogFormat *fmt                 = m_owner->m_format;
356   m_header                       = reinterpret_cast<LogBufferHeader *>(m_buffer);
357   m_header->cookie               = LOG_SEGMENT_COOKIE;
358   m_header->version              = LOG_SEGMENT_VERSION;
359   m_header->format_type          = fmt->type();
360   m_header->entry_count          = 0;
361   m_header->low_timestamp        = LogUtils::timestamp();
362   m_header->high_timestamp       = 0;
363   m_header->log_object_signature = m_owner->get_signature();
364   m_header->log_object_flags     = m_owner->get_flags();
365 #if defined(LOG_BUFFER_TRACKING)
366   m_header->id = lrand48();
367 #endif // defined(LOG_BUFFER_TRACKING)
368 
369   //
370   // The remaining header fields actually point into the data section of
371   // the buffer.  Write the data into the buffer and update the total
372   // size of the buffer header.
373   //
374 
375   header_len = sizeof(LogBufferHeader); // at least ...
376 
377   m_header->fmt_name_offset      = 0;
378   m_header->fmt_fieldlist_offset = 0;
379   m_header->fmt_printf_offset    = 0;
380   m_header->src_hostname_offset  = 0;
381   m_header->log_filename_offset  = 0;
382 
383   if (fmt->name()) {
384     m_header->fmt_name_offset = header_len;
385     header_len += add_header_str(fmt->name(), &m_buffer[header_len], m_size - header_len);
386   }
387   if (fmt->fieldlist()) {
388     m_header->fmt_fieldlist_offset = header_len;
389     header_len += add_header_str(fmt->fieldlist(), &m_buffer[header_len], m_size - header_len);
390   }
391   if (fmt->printf_str()) {
392     m_header->fmt_printf_offset = header_len;
393     header_len += add_header_str(fmt->printf_str(), &m_buffer[header_len], m_size - header_len);
394   }
395   if (cfg->hostname) {
396     m_header->src_hostname_offset = header_len;
397     header_len += add_header_str(cfg->hostname, &m_buffer[header_len], m_size - header_len);
398   }
399   if (m_owner->get_base_filename()) {
400     m_header->log_filename_offset = header_len;
401     header_len += add_header_str(m_owner->get_base_filename(), &m_buffer[header_len], m_size - header_len);
402   }
403   // update the rest of the header fields; make sure the header_len is
404   // correctly aligned, so that the first record will start on a legal
405   // alignment mark.
406   //
407 
408   header_len = INK_ALIGN_DEFAULT(header_len);
409 
410   m_header->byte_count  = header_len;
411   m_header->data_offset = header_len;
412 
413   return header_len;
414 }
415 
416 void
update_header_data()417 LogBuffer::update_header_data()
418 {
419   // only update the header if the LogBuffer did not receive its data
420   // upon construction (i.e., if m_unaligned_buffer was allocated)
421   //
422 
423   if (m_unaligned_buffer) {
424     m_header->entry_count    = m_state.s.num_entries;
425     m_header->byte_count     = m_state.s.offset;
426     m_header->high_timestamp = LogUtils::timestamp();
427   }
428 }
429 
430 /*-------------------------------------------------------------------------
431   LogBuffer::max_entry_bytes
432 
433   This static function simply returns the greatest number of bytes than an
434   entry can be and fit into a LogBuffer.
435   -------------------------------------------------------------------------*/
436 size_t
max_entry_bytes()437 LogBuffer::max_entry_bytes()
438 {
439   return (Log::config->log_buffer_size - sizeof(LogBufferHeader));
440 }
441 
442 /*-------------------------------------------------------------------------
443   LogBuffer::resolve_custom_entry
444   -------------------------------------------------------------------------*/
445 int
resolve_custom_entry(LogFieldList * fieldlist,char * printf_str,char * read_from,char * write_to,int write_to_len,long timestamp,long timestamp_usec,unsigned buffer_version,LogFieldList * alt_fieldlist,char * alt_printf_str)446 LogBuffer::resolve_custom_entry(LogFieldList *fieldlist, char *printf_str, char *read_from, char *write_to, int write_to_len,
447                                 long timestamp, long timestamp_usec, unsigned buffer_version, LogFieldList *alt_fieldlist,
448                                 char *alt_printf_str)
449 {
450   if (fieldlist == nullptr || printf_str == nullptr) {
451     return 0;
452   }
453 
454   int *readfrom_map = nullptr;
455 
456   if (alt_fieldlist && alt_printf_str) {
457     LogField *f, *g;
458     int n_alt_fields = alt_fieldlist->count();
459     int i            = 0;
460 
461     readfrom_map = static_cast<int *>(ats_malloc(n_alt_fields * sizeof(int)));
462     for (f = alt_fieldlist->first(); f; f = alt_fieldlist->next(f)) {
463       int readfrom_pos = 0;
464       bool found_match = false;
465       for (g = fieldlist->first(); g; g = fieldlist->next(g)) {
466         if (strcmp(f->symbol(), g->symbol()) == 0) {
467           found_match       = true;
468           readfrom_map[i++] = readfrom_pos;
469           break;
470         }
471         // TODO handle readfrom_pos properly
472         // readfrom_pos += g->size();
473       }
474       if (!found_match) {
475         Note("Alternate format contains a field (%s) not in the "
476              "format logged",
477              f->symbol());
478         break;
479       }
480     }
481   }
482   //
483   // Loop over the printf_str, copying everything to the write_to buffer
484   // except the LOG_FIELD_MARKER characters.  When we reach those, we
485   // substitute the string from the unmarshal routine of the current
486   // LogField object, obtained from the fieldlist.
487   //
488 
489   LogField *field     = fieldlist->first();
490   LogField *lastField = nullptr;                                // For debug message.
491   int markCount       = 0;                                      // For debug message.
492   int printf_len      = static_cast<int>(::strlen(printf_str)); // OPTIMIZE
493   int bytes_written   = 0;
494   int res, i;
495 
496   const char *buffer_size_exceeded_msg = "Traffic Server is skipping the current log entry because its size "
497                                          "exceeds the maximum line (entry) size for an ascii log buffer";
498 
499   for (i = 0; i < printf_len; i++) {
500     if (printf_str[i] == LOG_FIELD_MARKER) {
501       ++markCount;
502       if (field != nullptr) {
503         char *to = &write_to[bytes_written];
504         res      = field->unmarshal(&read_from, to, write_to_len - bytes_written);
505 
506         if (res < 0) {
507           Note("%s", buffer_size_exceeded_msg);
508           bytes_written = 0;
509           break;
510         }
511 
512         bytes_written += res;
513         lastField = field;
514         field     = fieldlist->next(field);
515       } else {
516         ts::LocalBufferWriter<10 * 1024> bw;
517         if (auto bs = fieldlist->badSymbols(); bs.size() > 0) {
518           bw << " (likely due to bad symbols " << bs << " in log format)";
519         }
520         Note("There are more field markers than fields%*s;"
521              " cannot process log entry '%.*s'. Last field = '%s' printf_str='%s' pos=%d/%d count=%d alt_printf_str='%s'",
522              static_cast<int>(bw.size()), bw.data(), bytes_written, write_to, lastField == nullptr ? "*" : lastField->symbol(),
523              printf_str == nullptr ? "*NULL*" : printf_str, i, printf_len, markCount,
524              alt_printf_str == nullptr ? "*NULL*" : alt_printf_str);
525         bytes_written = 0;
526         break;
527       }
528     } else {
529       if (1 + bytes_written < write_to_len) {
530         write_to[bytes_written++] = printf_str[i];
531       } else {
532         Note("%s", buffer_size_exceeded_msg);
533         bytes_written = 0;
534         break;
535       }
536     }
537   }
538 
539   ats_free(readfrom_map);
540   return bytes_written;
541 }
542 
543 /*-------------------------------------------------------------------------
544   LogBuffer::to_ascii
545 
546   This routine converts a log entry into an ascii string in the buffer
547   space provided, and returns the length of the new string (not including
548   trailing null, like strlen).
549   -------------------------------------------------------------------------*/
550 int
to_ascii(LogEntryHeader * entry,LogFormatType type,char * buf,int buf_len,const char * symbol_str,char * printf_str,unsigned buffer_version,const char * alt_format)551 LogBuffer::to_ascii(LogEntryHeader *entry, LogFormatType type, char *buf, int buf_len, const char *symbol_str, char *printf_str,
552                     unsigned buffer_version, const char *alt_format)
553 {
554   ink_assert(entry != nullptr);
555   ink_assert(type == LOG_FORMAT_CUSTOM || type == LOG_FORMAT_TEXT);
556   ink_assert(buf != nullptr);
557 
558   char *read_from; // keeps track of where we're reading from entry
559   char *write_to;  // keeps track of where we're writing into buf
560 
561   read_from = reinterpret_cast<char *>(entry) + sizeof(LogEntryHeader);
562   write_to  = buf;
563 
564   if (type == LOG_FORMAT_TEXT) {
565     //
566     // text log entries are just strings, so simply move it into the
567     // format buffer.
568     //
569     return ink_strlcpy(write_to, read_from, buf_len);
570   }
571   //
572   // We no longer make the distinction between custom vs pre-defined
573   // logging formats in converting to ASCII.  This way we're sure to
574   // always be using the correct printf string and symbols for this
575   // buffer since we get it from the buffer header.
576   //
577   // We want to cache the unmarshaling "plans" so that we don't have to
578   // re-create them each time.  We can use the symbol string as a key to
579   // these stored plans.
580   //
581 
582   int i;
583   LogFieldList *fieldlist = nullptr;
584   bool delete_fieldlist_p = false; // need to free the fieldlist?
585 
586   for (i = 0; i < fieldlist_cache_entries; i++) {
587     if (strcmp(symbol_str, fieldlist_cache[i].symbol_str) == 0) {
588       Debug("log-fieldlist", "Fieldlist for %s found in cache, #%d", symbol_str, i);
589       fieldlist = fieldlist_cache[i].fieldlist;
590       break;
591     }
592   }
593 
594   if (!fieldlist) {
595     Debug("log-fieldlist", "Fieldlist for %s not found; creating ...", symbol_str);
596     fieldlist = new LogFieldList;
597     ink_assert(fieldlist != nullptr);
598     bool contains_aggregates = false;
599     LogFormat::parse_symbol_string(symbol_str, fieldlist, &contains_aggregates);
600 
601     if (fieldlist_cache_entries < FIELDLIST_CACHE_SIZE) {
602       Debug("log-fieldlist", "Fieldlist cached as entry %d", fieldlist_cache_entries);
603       fieldlist_cache[fieldlist_cache_entries].fieldlist  = fieldlist;
604       fieldlist_cache[fieldlist_cache_entries].symbol_str = ats_strdup(symbol_str);
605       fieldlist_cache_entries++;
606     } else {
607       delete_fieldlist_p = true;
608     }
609   }
610 
611   LogFieldList *alt_fieldlist = nullptr;
612   char *alt_printf_str        = nullptr;
613   char *alt_symbol_str        = nullptr;
614   bool bad_alt_format         = false;
615 
616   if (alt_format) {
617     int n_alt_fields = LogFormat::parse_format_string(alt_format, &alt_printf_str, &alt_symbol_str);
618     if (n_alt_fields < 0) {
619       Note("Error parsing alternate format string: %s", alt_format);
620       bad_alt_format = true;
621     }
622 
623     if (!bad_alt_format) {
624       alt_fieldlist      = new LogFieldList;
625       bool contains_aggs = false;
626       int n_alt_fields2  = LogFormat::parse_symbol_string(alt_symbol_str, alt_fieldlist, &contains_aggs);
627       if (n_alt_fields2 > 0 && contains_aggs) {
628         Note("Alternative formats not allowed to contain aggregates");
629         bad_alt_format = true;
630         ;
631       }
632     }
633   }
634 
635   if (bad_alt_format) {
636     delete alt_fieldlist;
637     ats_free(alt_printf_str);
638     ats_free(alt_symbol_str);
639     alt_fieldlist  = nullptr;
640     alt_printf_str = nullptr;
641     alt_symbol_str = nullptr;
642   }
643 
644   int ret = resolve_custom_entry(fieldlist, printf_str, read_from, write_to, buf_len, entry->timestamp, entry->timestamp_usec,
645                                  buffer_version, alt_fieldlist, alt_printf_str);
646 
647   delete alt_fieldlist;
648   ats_free(alt_printf_str);
649   ats_free(alt_symbol_str);
650   if (delete_fieldlist_p) {
651     delete fieldlist;
652   }
653 
654   return ret;
655 }
656 
657 /*-------------------------------------------------------------------------
658   LogBufferList
659 
660   The operations on this list need to be atomic because the buffers are
661   added by client threads and removed by the logging thread.  This is
662   accomplished by protecting the operations with a mutex.
663 
664   Also, the list must offer FIFO semantics so that a buffers are removed
665   from the list in the same order that they are added, so that timestamp
666   ordering in the log file is preserved.
667   -------------------------------------------------------------------------*/
668 
669 /*-------------------------------------------------------------------------
670   LogBufferList::LogBufferList
671   -------------------------------------------------------------------------*/
672 
LogBufferList()673 LogBufferList::LogBufferList()
674 {
675   m_size = 0;
676   ink_mutex_init(&m_mutex);
677 }
678 
679 /*-------------------------------------------------------------------------
680   LogBufferList::~LogBufferList
681   -------------------------------------------------------------------------*/
682 
~LogBufferList()683 LogBufferList::~LogBufferList()
684 {
685   LogBuffer *lb;
686   while ((lb = get()) != nullptr) {
687     delete lb;
688   }
689   m_size = 0;
690   ink_mutex_destroy(&m_mutex);
691 }
692 
693 /*-------------------------------------------------------------------------
694   LogBufferList::add (or enqueue)
695   -------------------------------------------------------------------------*/
696 
697 void
add(LogBuffer * lb)698 LogBufferList::add(LogBuffer *lb)
699 {
700   ink_assert(lb != nullptr);
701 
702   ink_mutex_acquire(&m_mutex);
703   m_buffer_list.enqueue(lb);
704   ink_assert(m_size >= 0);
705   m_size++;
706   ink_mutex_release(&m_mutex);
707 }
708 
709 /*-------------------------------------------------------------------------
710   LogBufferList::get (or dequeue)
711   -------------------------------------------------------------------------*/
712 
713 LogBuffer *
get()714 LogBufferList::get()
715 {
716   LogBuffer *lb;
717 
718   ink_mutex_acquire(&m_mutex);
719   lb = m_buffer_list.dequeue();
720   if (lb != nullptr) {
721     m_size--;
722     ink_assert(m_size >= 0);
723   }
724   ink_mutex_release(&m_mutex);
725   return lb;
726 }
727 
728 /*-------------------------------------------------------------------------
729   -------------------------------------------------------------------------*/
730 
731 LogEntryHeader *
next()732 LogBufferIterator::next()
733 {
734   LogEntryHeader *ret_val = nullptr;
735   LogEntryHeader *entry   = reinterpret_cast<LogEntryHeader *>(m_next);
736 
737   if (entry) {
738     if (m_iter_entry_count < m_buffer_entry_count) {
739       m_next += entry->entry_len;
740       ++m_iter_entry_count;
741       ret_val = entry;
742     }
743   }
744 
745   return ret_val;
746 }
747