Willy Tarreau a58af5b0a1 MINOR: dynbuf: switch allocation and release to macros to better track users
When building with DEBUG_MEM_STATS, we only see b_alloc() and b_free() as
users of the "buffer" pool, because all call places rely on these more
convenient functions. It's annoying because it makes it very hard to see
which parts of the code are consuming buffers.

By switching the b_alloc() and b_free() inline functions to macros, we
can now finally track the users of struct buffer, e.g:

  mux_h1.c:513            P_FREE  size:   1275002880  calls:     38910  size/call:  32768 buffer
  mux_h1.c:498           P_ALLOC  size:   1912438784  calls:     58363  size/call:  32768 buffer
  stream.c:763            P_FREE  size:   4121493504  calls:    125778  size/call:  32768 buffer
  stream.c:759            P_FREE  size:   2061697024  calls:     62918  size/call:  32768 buffer
  stream.c:742           P_ALLOC  size:   3341123584  calls:    101963  size/call:  32768 buffer
  stream.c:632            P_FREE  size:   1275068416  calls:     38912  size/call:  32768 buffer
  stream.c:631            P_FREE  size:    637435904  calls:     19453  size/call:  32768 buffer
  channel.h:850          P_ALLOC  size:   4116480000  calls:    125625  size/call:  32768 buffer
  channel.h:850          P_ALLOC  size:       720896  calls:        22  size/call:  32768 buffer
  dynbuf.c:55             P_FREE  size:        65536  calls:         2  size/call:  32768 buffer

Let's do this since it doesn't change anything for the output code
(beyond adding the call places). Interestingly the code even got
slightly smaller now.
2022-11-16 11:44:26 +01:00

129 lines
3.8 KiB
C

/*
* include/haproxy/dynbuf.h
* Buffer management functions.
*
* Copyright (C) 2000-2020 Willy Tarreau - w@1wt.eu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_DYNBUF_H
#define _HAPROXY_DYNBUF_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <import/ist.h>
#include <haproxy/activity.h>
#include <haproxy/api.h>
#include <haproxy/buf.h>
#include <haproxy/chunk.h>
#include <haproxy/dynbuf-t.h>
#include <haproxy/pool.h>
extern struct pool_head *pool_head_buffer;
int init_buffer(void);
void buffer_dump(FILE *o, struct buffer *b, int from, int to);
/*****************************************************************/
/* These functions are used to compute various buffer area sizes */
/*****************************************************************/
/* Return 1 if the buffer has less than 1/4 of its capacity free, otherwise 0 */
static inline int buffer_almost_full(const struct buffer *buf)
{
if (b_is_null(buf))
return 0;
return b_almost_full(buf);
}
/**************************************************/
/* Functions below are used for buffer allocation */
/**************************************************/
/* Ensures that <buf> is allocated, or allocates it. If no memory is available,
* ((char *)1) is assigned instead with a zero size. The allocated buffer is
* returned, or NULL in case no memory is available.
*/
#define b_alloc(_buf) \
({ \
char *_area; \
struct buffer *_retbuf = _buf; \
\
if (!_retbuf->size) { \
*_retbuf = BUF_WANTED; \
_area = pool_alloc_flag(pool_head_buffer, POOL_F_NO_POISON); \
if (unlikely(!_area)) { \
activity[tid].buf_wait++; \
_retbuf = NULL; \
} \
else { \
_retbuf->area = _area; \
_retbuf->size = pool_head_buffer->size; \
} \
} \
_retbuf; \
})
/* Releases buffer <buf> (no check of emptiness). The buffer's head is marked
* empty.
*/
#define __b_free(_buf) \
do { \
char *area = (_buf)->area; \
\
/* let's first clear the area to save an occasional "show sess all" \
* glancing over our shoulder from getting a dangling pointer. \
*/ \
*(_buf) = BUF_NULL; \
__ha_barrier_store(); \
pool_free(pool_head_buffer, area); \
} while (0) \
/* Releases buffer <buf> if allocated, and marks it empty. */
#define b_free(_buf) \
do { \
if ((_buf)->size) \
__b_free((_buf)); \
} while (0)
/* Offer one or multiple buffer currently belonging to target <from> to whoever
* needs one. Any pointer is valid for <from>, including NULL. Its purpose is
* to avoid passing a buffer to oneself in case of failed allocations (e.g.
* need two buffers, get one, fail, release it and wake up self again). In case
* of normal buffer release where it is expected that the caller is not waiting
* for a buffer, NULL is fine. It will wake waiters on the current thread only.
*/
void __offer_buffers(void *from, unsigned int count);
static inline void offer_buffers(void *from, unsigned int count)
{
if (!LIST_ISEMPTY(&th_ctx->buffer_wq))
__offer_buffers(from, count);
}
#endif /* _HAPROXY_DYNBUF_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/