On Sun, Jul 08, 2001 at 10:11:11AM -0700, Justin Erenkrantz wrote:
> In the effort to place my code where my mouth is, I'm implementing this.
> Patch posting shortly.  -- justin

memory/unix/apr_sms_replay.c and a new apr_sms_pool.c that cleans up
things slightly (create_sms function centralizes which SMS we are using
- not sure if that is helpful) and define APR_POOL_REPLAY to do your
output format.  

I didn't bother to clean up the apr_instrument function, but if I would
actually commit this, I'd clean it up slightly.  =)  It's a hack.

You'll need David's other patches to do the POOLS_ARE_SMS.  -- justin
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

#include "apr.h"
#include "apr_general.h"
#include "apr_private.h"
#include "apr_sms.h"
#include "apr_sms_replay.h"
#include "apr_lock.h"
#include <stddef.h> /* for NULL */
#include <stdarg.h> /* for va_list */
#include <unistd.h> /* for getpid */
#include "sms_private.h"

static const char *module_identity = "REPLAY";

/*
 * A SMS that will write debugging information to a file.  This will work 
 * with the testpool code to allow the allocation to be replayed.
 */

/* INTERNALLY USED STRUCTURES */
typedef struct block_t
{
    struct block_t  *next;
    struct block_t **ref;
} block_t;

typedef struct apr_sms_replay_t
{
    apr_sms_t   sms_hdr;
    block_t    *blocks;
    apr_lock_t *lock;
} apr_sms_replay_t;

#define SIZEOF_BLOCK_T        APR_ALIGN_DEFAULT(sizeof(block_t))
#define SIZEOF_SMS_REPLAY_T   APR_ALIGN_DEFAULT(sizeof(apr_sms_replay_t))

#define BLOCK_T(mem)          ((block_t *)(mem))
#define SMS_REPLAY_T(sms)     ((apr_sms_replay_t *)(sms))

#define INSERT_BLOCK(block, tms) \
    if (tms->lock) \
        apr_lock_acquire(tms->lock); \
    \
    block->ref = &tms->blocks; \
    if ((block->next = tms->blocks) != NULL) \
        block->next->ref = &block->next; \
    tms->blocks = block; \
    \
    if (tms->lock) \
        apr_lock_release(tms->lock);

#define REMOVE_BLOCK(block, tms) \
    if (tms->lock) \
        apr_lock_acquire(tms->lock); \
    \
    if (block->next) \
        block->next->ref = block->ref; \
    \
    if (tms->lock) \
        apr_lock_release(tms->lock);

static void apr_sms_instrument(const char*fn, apr_sms_t *sms, 
                               const char*format,... )
{
    static volatile int bfirst=1;
    char buffer[1000];
    char buffer2[1000];
    va_list ap;
    apr_os_thread_t pthread = apr_os_thread_current();
    pid_t pid = getpid();
    struct timeval tv;
    gettimeofday(&tv, NULL);
    if (bfirst==1) {
        bfirst=0;
        fprintf(stderr, "#APR_INSTRUMENT:PID,THREAD,SECS,USEC,POOL,FN,ARGS\n");
    }
    va_start(ap, format);
    vsnprintf(buffer, sizeof(buffer), format, ap);
    va_end(ap);
    snprintf(buffer2, sizeof(buffer2), 
             "APR_INSTRUMENT:POOL,%lu,%lu,%ld,%ld,%lu,%s,%s\n",
             pid, pthread, tv.tv_sec, tv.tv_usec, sms, fn, buffer);
    fputs(buffer2,stderr);
}

static void *apr_sms_replay_malloc(apr_sms_t *sms,
                                     apr_size_t size)
{
    void *mem;

    apr_sms_instrument("apr_palloc", sms, "%lu", size);

    size = APR_ALIGN_DEFAULT(size) + SIZEOF_BLOCK_T;
    mem = apr_sms_malloc(sms->parent, size);
    if (!mem)
        return NULL;

    INSERT_BLOCK(BLOCK_T(mem), SMS_REPLAY_T(sms))
    
    mem = (char *)mem + SIZEOF_BLOCK_T;

    return mem;
}

static void *apr_sms_replay_calloc(apr_sms_t *sms, 
                                   apr_size_t size)
{
    void *mem;

    apr_sms_instrument("apr_pcalloc", sms, "%lu", size);

    size = APR_ALIGN_DEFAULT(size) + SIZEOF_BLOCK_T;
    mem = apr_sms_calloc(sms->parent, size);
    if (!mem)
        return NULL;

    INSERT_BLOCK(BLOCK_T(mem), SMS_REPLAY_T(sms))
    
    mem = (char *)mem + SIZEOF_BLOCK_T;

    return mem;
}

static void *apr_sms_replay_realloc(apr_sms_t *sms,
                                    void *mem, apr_size_t size)
{
    block_t *block;
    
    block = BLOCK_T(mem - SIZEOF_BLOCK_T);

    REMOVE_BLOCK(block, SMS_REPLAY_T(sms))

    size = APR_ALIGN_DEFAULT(size) + SIZEOF_BLOCK_T;
    mem = apr_sms_realloc(sms->parent, block, size);
    if (!mem)
        return NULL;

    INSERT_BLOCK(BLOCK_T(mem), SMS_REPLAY_T(sms))

    mem = (char *)mem + SIZEOF_BLOCK_T;

    return mem;
}

static apr_status_t apr_sms_replay_free(apr_sms_t *sms,
                                          void *mem)
{
    mem = (char *)mem - SIZEOF_BLOCK_T; 

    REMOVE_BLOCK(BLOCK_T(mem), SMS_REPLAY_T(sms));

    return apr_sms_free(sms->parent, mem);
}

static apr_status_t apr_sms_replay_reset(apr_sms_t *sms)
{
    block_t *block;
    apr_status_t rv = APR_SUCCESS;
 
    apr_sms_instrument("apr_pool_clear", sms, "-");

    if (SMS_REPLAY_T(sms)->lock)
        apr_lock_acquire(SMS_REPLAY_T(sms)->lock);
    
    while ((block = SMS_REPLAY_T(sms)->blocks) != NULL) {
        if ((*block->ref = block->next) != NULL)
            block->next->ref = block->ref;
        
        if ((rv = apr_sms_free(sms->parent, block)) != APR_SUCCESS)
            break;
    }
    
    if (SMS_REPLAY_T(sms)->lock)
        apr_lock_release(SMS_REPLAY_T(sms)->lock);

    return rv;
}

static apr_status_t apr_sms_replay_pre_destroy(apr_sms_t *sms)
{
    /* This function WILL alwways be called.  However, be aware that the
     * main sms destroy function knows that it's not wise to try and destroy
     * the same piece of memory twice, so the destroy function in a child won't
     * neccesarily be called.  To guarantee we destroy the lock it's therefore
     * destroyed here.
     */
 
    if (SMS_REPLAY_T(sms)->lock) {
        apr_lock_acquire(SMS_REPLAY_T(sms)->lock);
        apr_lock_destroy(SMS_REPLAY_T(sms)->lock);
        SMS_REPLAY_T(sms)->lock = NULL;
    }
    
    return APR_SUCCESS;    
}

static apr_status_t apr_sms_replay_destroy(apr_sms_t *sms)
{
    apr_status_t rv;

    if ((rv = apr_sms_reset(sms)) != APR_SUCCESS)
        return rv;
    
    return apr_sms_free(sms->parent, sms);
}

#if APR_HAS_THREADS
static apr_status_t apr_sms_replay_thread_register(apr_sms_t *sms,
                                                     apr_os_thread_t thread)
{
    if (!SMS_REPLAY_T(sms)->lock && sms->threads > 1)
        return apr_lock_create(&SMS_REPLAY_T(sms)->lock,
                               APR_MUTEX, APR_LOCKALL,
                               NULL, sms->pool);
    return APR_SUCCESS;
}

static apr_status_t apr_sms_replay_thread_unregister(apr_sms_t *sms,
                                                       apr_os_thread_t thread)
{
    return APR_SUCCESS;
}
#endif /* APR_HAS_THREADS */


APR_DECLARE(apr_status_t) apr_sms_replay_create(apr_sms_t **sms, 
                                                  apr_sms_t *pms)
{
    apr_sms_t *new_sms;
    apr_status_t rv;

    *sms = NULL;
    /* We're not a top level module, ie we have a parent, so
     * we allocate the memory for the structure from our parent.
     * This is safe as we shouldn't outlive our parent...
     */
    new_sms = apr_sms_calloc(pms, SIZEOF_SMS_REPLAY_T);

    if (!new_sms)
        return APR_ENOMEM;

    if ((rv = apr_sms_init(new_sms, pms)) != APR_SUCCESS)
        return rv;

    new_sms->malloc_fn            = apr_sms_replay_malloc;
    new_sms->calloc_fn            = apr_sms_replay_calloc;
    new_sms->realloc_fn           = apr_sms_replay_realloc;
    new_sms->free_fn              = apr_sms_replay_free;
    new_sms->reset_fn             = apr_sms_replay_reset;
    new_sms->pre_destroy_fn       = apr_sms_replay_pre_destroy;
    new_sms->destroy_fn           = apr_sms_replay_destroy;
#if APR_HAS_THREADS
    new_sms->thread_register_fn   = apr_sms_replay_thread_register;
    new_sms->thread_unregister_fn = apr_sms_replay_thread_unregister;
#endif /* APR_HAS_THREADS */
    new_sms->identity             = module_identity;
    
    apr_sms_post_init(new_sms);

    apr_sms_instrument("apr_pool_create", pms, "%lu", new_sms);

    *sms = new_sms;
    return APR_SUCCESS;
}
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

#define APR_POOL_REPLAY

#include "apr.h"
#include "apr_pools.h" /* includes apr_sms.h" */
#include "apr_sms_trivial.h"
#ifdef APR_POOL_REPLAY
#include "apr_sms_replay.h"
#endif
#include "apr_errno.h"
#include "apr_lock.h"
#include "apr_portable.h"
#include "apr_lib.h" /* for apr_vformatter */

#include "sms_private.h"

static int initialized = 0;
static apr_pool_t *permanent_pool = NULL;

static apr_status_t create_pool(apr_pool_t **newpool, apr_pool_t *p)
{
#ifdef APR_POOL_REPLAY
    apr_pool_t *temppool;

    apr_sms_trivial_create(&temppool, p);
    return apr_sms_replay_create(newpool, temppool);
#else
    return apr_sms_trivial_create(newpool, p);
#endif
}

APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool, apr_pool_t *p)
{
    if (!initialized)
        /* Hmm, if we are given a parent here, is this correct?
         * It should never happen, so we're probably OK....
         */
        return apr_sms_std_create(newpool);

    return create_pool(newpool, p ? p : permanent_pool);
}
    
APR_DECLARE(apr_pool_t *) apr_pool_sub_make(apr_pool_t * p,
                                           apr_abortfunc_t abort)
{
    apr_pool_t *np;

    if (create_pool(&np, p) != APR_SUCCESS)
        return NULL;

    apr_sms_set_abort(abort, np);

    return np;
}

APR_DECLARE(void) apr_pool_cleanup_register(apr_pool_t *pool,
                                            const void *data,
                                         apr_status_t (*plain_cleanup)(void*),
                                         apr_status_t (*child_cleanup)(void*))
{
    if (plain_cleanup == child_cleanup) {
        /* we only need to register one as an ALL_CLEANUP */
        apr_sms_cleanup_register(pool, APR_ALL_CLEANUPS, data, plain_cleanup);
        return;
    }
    if (plain_cleanup)
        apr_sms_cleanup_register(pool, APR_GENERAL_CLEANUP, data,
                                        plain_cleanup);

    if (child_cleanup)
        apr_sms_cleanup_register(pool, APR_CHILD_CLEANUP, data,
                                        child_cleanup);
}

APR_DECLARE(void) apr_pool_cleanup_for_exec(void)
{
#if !defined(WIN32) && !defined(OS2)
    /* See note in apr_pools.c for why we do this :) */
    apr_sms_cleanup_run_type(permanent_pool, APR_CHILD_CLEANUP);
#endif
}

APR_DECLARE(apr_status_t) apr_pool_alloc_init(apr_pool_t *gp)
{
    initialized = 1;
    return create_pool(&permanent_pool, gp);
}

APR_DECLARE(void) apr_pool_alloc_term(apr_pool_t *gp)
{
    apr_sms_destroy(permanent_pool);
    /* so, are we still initialized after this???? */
}

APR_DECLARE(int) apr_pool_is_ancestor(apr_pool_t *a, apr_pool_t *b)
{
    while (b && b != a)
        b = b->parent;

    return b == a;
}

/* This stuff needs to be reviewed, but here it is :) */

struct psprintf_data {
    apr_vformatter_buff_t  vbuff;
    char *base;
    apr_sms_t *sms;
};

static int psprintf_flush(apr_vformatter_buff_t *vbuff)
{
    struct psprintf_data *ps = (struct psprintf_data*)vbuff;
    apr_size_t size;
    char *ptr;

    size = (char*) ps->vbuff.curpos - ps->base;
    ptr = apr_sms_realloc(ps->sms, ps->base, 2*size);
    if (ptr == NULL) {
        fputs("[psprintf_flush] Ouch!  Out of memory!\n", stderr);
        exit(1);
    }
    ps->base = ptr;
    ps->vbuff.curpos = ptr + size;
    ps->vbuff.endpos = ptr + 2 * size - 1;
    return 0;
}

APR_DECLARE(char *) apr_pvsprintf(apr_pool_t *p, const char *fmt, va_list ap)
{
    struct psprintf_data ps;
    void *ptr;

    ps.sms = (apr_sms_t*)p;
    ps.base = apr_sms_malloc(ps.sms, 512);
    if (ps.base == NULL) {
        fputs("[apr_pvsprintf] Ouch! Out of memory!\n", stderr);
        exit(1);
    }
    ps.vbuff.curpos = ps.base;
    ps.vbuff.endpos = ps.base + 511;
    apr_vformatter(psprintf_flush, &ps.vbuff, fmt, ap);
    *ps.vbuff.curpos++ = '\0';
    ptr = ps.base;
    ptr = apr_sms_realloc(ps.sms, ptr, (char*)ps.vbuff.curpos - (char*)ptr);
    if (ptr == NULL) {
        fputs("[apr_pvsprintf #2] Ouch! Out of memory!\n", stderr);
        exit(1);
    }
    return (char*)ptr;
}

APR_DECLARE_NONSTD(char*) apr_psprintf(apr_pool_t *p, const char *fmt, ...)
{
    va_list ap;
    char *res;

    va_start(ap,fmt);
    res = apr_pvsprintf(p, fmt, ap);
    va_end(ap);
    return res;
}

APR_DECLARE(void) apr_pool_note_subprocess(apr_pool_t *a, apr_proc_t *pid,
                                           enum kill_conditions how)
{
    struct process_chain *newpc = (struct process_chain*)
        apr_sms_malloc(a, sizeof(struct process_chain));

    newpc->pid       = pid;
    newpc->kill_how  = how;
    newpc->next      = a -> subprocesses;
    a->subprocesses  = newpc;
}

Reply via email to