Apache-Scoreboard

 view release on metacpan or  search on metacpan

Scoreboard.xs  view on Meta::CPAN

#define PERL_NO_GET_CONTEXT /* we want efficiency */
#include "mod_perl.h"
#include "modperl_xs_sv_convert.h"
#include "modperl_xs_typedefs.h"
#include "modperl_xs_util.h"

#include "scoreboard.h"

/* XXX: currently only for mp2 */
#define SB_TRACE_DO 0

#if SB_TRACE_DO && defined(MP_TRACE)
#define SB_TRACE modperl_trace
#else
#define SB_TRACE if (0) modperl_trace
#endif

/* scoreboard */
typedef struct {
    scoreboard *sb;
    apr_pool_t *pool;
    int server_limit;
    int thread_limit;
} modperl_scoreboard_t;

typedef struct {
    worker_score *record;
    int parent_idx;
    int worker_idx;
} modperl_worker_score_t;

/* XXX: notice that here we reference a struct living in a different
 * perl object ($image), so if that object is destroyed earlier we get
 * a segfault. a possible solution: inline modperl_scoreboard_t in
 * modperl_parent_score_t (can't create a dependency inside $image,
 * since there can be many objects referencing it, will require a
 * complicated real ref counting)
 */
typedef struct {
    process_score *record;
    int idx;
    modperl_scoreboard_t *image;
} modperl_parent_score_t;

typedef modperl_scoreboard_t   *Apache__Scoreboard;
typedef modperl_worker_score_t *Apache__ScoreboardWorkerScore;
typedef modperl_parent_score_t *Apache__ScoreboardParentScore;

static char status_flags[SERVER_NUM_STATUS];

#define server_limit(image) image->server_limit
#define thread_limit(image) image->thread_limit
    
#define scoreboard_up_time(image)                               \
    (apr_uint32_t) apr_time_sec(                                \
        apr_time_now() - image->sb->global->restart_time);

#define parent_score_pid(mps)  mps->record->pid

#define worker_score_most_recent(mws)                                   \
    (apr_uint32_t) apr_time_sec(apr_time_now() - mws->record->last_used);

/* XXX: as of 20031219, tid is not maintained in scoreboard */
#if APR_HAS_THREADS
#define worker_score_tid(mws)             mws->record->tid
#else
#define worker_score_tid(mws)             NULL
#endif

#define worker_score_thread_num(mws)      mws->record->thread_num
#define worker_score_access_count(mws)    mws->record->access_count
#define worker_score_bytes_served(mws)    mws->record->bytes_served
#define worker_score_my_access_count(mws) mws->record->my_access_count
#define worker_score_my_bytes_served(mws) mws->record->my_bytes_served
#define worker_score_conn_bytes(mws)      mws->record->conn_bytes
#define worker_score_conn_count(mws)      mws->record->conn_count
#define worker_score_client(mws)          mws->record->client
#define worker_score_request(mws)         mws->record->request
#define worker_score_vhost(mws)           mws->record->vhost

/* a worker that have served/serves at least one request and isn't
 * dead yet */
#define LIVE_WORKER(ws) !(ws->access_count == 0 && ws->status == SERVER_DEAD)

/* a worker that does something at this very moment */
#define ACTIVE_WORKER(ws)                                               \
    !(ws->access_count == 0 &&                                          \
      (ws->status == SERVER_DEAD || ws->status == SERVER_READY))

#include "apxs/send.c"

static void status_flags_init(void)
{
    status_flags[SERVER_DEAD]           = '.';
    status_flags[SERVER_READY]          = '_';
    status_flags[SERVER_STARTING]       = 'S';
    status_flags[SERVER_BUSY_READ]      = 'R';
    status_flags[SERVER_BUSY_WRITE]     = 'W';
    status_flags[SERVER_BUSY_KEEPALIVE] = 'K';
    status_flags[SERVER_BUSY_LOG]       = 'L';
    status_flags[SERVER_BUSY_DNS]       = 'D';
    status_flags[SERVER_CLOSING]        = 'C';
    status_flags[SERVER_GRACEFUL]       = 'G';
    status_flags[SERVER_IDLE_KILL]      = 'I';
}

static void constants_init(pTHX)
{
      HV *stash;
      int server_limit, thread_limit;

      /* SERVER_LIMIT and THREAD_LIMIT constants are deprecated, use
       * $image->server_limit and $image->thread_limit instead */
#ifndef DUMMY_SCOREBOARD
    ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
#else
    /* XXX: how can we figure out that data w/o having an access to
     * ap_mpm_query? */
    server_limit = 0;
    thread_limit = 0;
#endif
    
    stash = gv_stashpv("Apache::Const", TRUE);
    newCONSTSUB(stash, "SERVER_LIMIT", newSViv(server_limit));
    
    stash = gv_stashpv("Apache::Const", TRUE);
    newCONSTSUB(stash, "THREAD_LIMIT", newSViv(thread_limit));

    stash = gv_stashpv("Apache::Scoreboard", TRUE);
    newCONSTSUB(stash, "REMOTE_SCOREBOARD_TYPE",
                newSVpv(REMOTE_SCOREBOARD_TYPE, 0));

}

MP_INLINE
static worker_score *my_get_scoreboard_worker(pTHX_
                                              modperl_scoreboard_t *image,
                                              int x, int y)
{
    if (((x < 0) || (image->server_limit < x)) ||
        ((y < 0) || (image->thread_limit < y))) {
        Perl_croak(aTHX_ "worker score [%d][%d] is out of limit", x, y);
    }
    return &image->sb->servers[x][y];
}

MP_INLINE
static process_score *my_get_scoreboard_process(pTHX_
                                                modperl_scoreboard_t *image,
                                                int x)
{
    if ((x < 0) || (image->server_limit < x)) {
        Perl_croak(aTHX_ "parent score [%d] is out of limit", x);
    }
    return &image->sb->parent[x];
}

static void image_sanity_check(pTHX)
{
#ifdef DUMMY_SCOREBOARD
    Perl_croak(aTHX_ "Don't call the image() method when not"
               "running under mod_perl");
#endif
}

#ifdef DUMMY_SCOREBOARD
#define MY_WARN fprintf(stderr,
#else
#define MY_WARN ap_log_error(APLOG_MARK, APLOG_ERR, 0, modperl_global_get_server_rec(),
#endif
      
#if 0
static void debug_dump_sb(modperl_scoreboard_t *image)
{
    int i, j;

    for (i = 0; i < image->server_limit; i++) {
        for (j = 0; j < image->thread_limit; j++) {
            worker_score *ws = &image->sb->servers[i][j];
            if (ws->access_count) {
                MY_WARN
                    "rcv %02d-%02d: stat: %c cnt: %d\n", i, j,
                    status_flags[ws->status],
                    (int)ws->access_count);
            }
        }
    }
}
#endif







MODULE = Apache::Scoreboard   PACKAGE = Apache::Scoreboard   PREFIX = scoreboard_

BOOT:
{

    /* XXX: this must be performed only once and before other threads are spawned.
     * but not sure. could be that need to use local storage.
     *
     */
    status_flags_init();
    
    constants_init(aTHX);
}

int
scoreboard_send(r)
    Apache2::RequestRec r


SV *
freeze(image)
    Apache::Scoreboard image

    PREINIT:
    int psize, ssize, tsize, msize, i;
    char buf[SIZE16*4];
    char *dptr, *ptr = buf;
    scoreboard *sb;

    CODE:
    sb = image->sb;
    
    psize = sizeof(process_score) * image->server_limit;
    msize = sizeof(worker_score)  * image->thread_limit;
    ssize = msize * image->server_limit;
    tsize = psize + ssize + sizeof(global_score) + sizeof(buf);
    /* fprintf(stderr, "sizes %d, %d, %d, %d, %d\n",
       psize, ssize, sizeof(global_score) , sizeof(buf), tsize); */
                 
    pack16(ptr, psize);
    ptr += SIZE16;
    pack16(ptr, ssize);
    ptr += SIZE16;
    pack16(ptr, image->server_limit);
    ptr += SIZE16;
    pack16(ptr, image->thread_limit);

    RETVAL = NEWSV(0, tsize);
    dptr = SvPVX(RETVAL);
    SvCUR_set(RETVAL, tsize+1);
    SvPOK_only(RETVAL);

    /* fill the data buffer with the data we want to freeze */
    Move(&buf[0],        dptr, sizeof(buf),          char);
    dptr += sizeof(buf);
    Move(&sb->parent[0], dptr, psize,                char);
    dptr += psize;
    for (i = 0; i < image->server_limit; i++) {
        Move(sb->servers[i], dptr, msize, char);
        dptr += msize;
    }
    Move(&sb->global,    dptr, sizeof(global_score), char);

    OUTPUT:
    RETVAL

Apache::Scoreboard
thaw(CLASS, pool, packet)
    SV *CLASS
    APR::Pool pool
    SV *packet

    PREINIT:
    modperl_scoreboard_t *image;
    scoreboard *sb;
    int psize, ssize;
    char *ptr;
    int i;
    
    CODE:
    if (!(SvOK(packet) && SvCUR(packet) > (SIZE16*2))) {
        XSRETURN_UNDEF;
    }

    CLASS = CLASS; /* avoid warnings */
 
    image = (modperl_scoreboard_t *)apr_pcalloc(pool, sizeof(*image));

    ptr = SvPVX(packet);
    psize = unpack16(ptr);
    ptr += SIZE16;
    ssize = unpack16(ptr);
    ptr += SIZE16;
    image->server_limit = unpack16(ptr);
    ptr += SIZE16;
    image->thread_limit = unpack16(ptr);
    ptr += SIZE16;

   /* MY_WARN
      "recv: sizes server_num=%d, thread_num=%d, psize=%d, "
                 "ssize=%d\n",
                 image->server_limit, image->thread_limit, psize, ssize);
   */

    sb = (scoreboard *)apr_palloc(pool, sizeof(scoreboard) +
                                   image->server_limit * sizeof(worker_score *));
    sb->parent  = (process_score *)Copy_pool(pool, ptr, psize, char);
    ptr += psize;

    sb->servers = (worker_score **)((char*)sb + sizeof(scoreboard));
    for (i = 0; i < image->server_limit; i++) {
        sb->servers[i] = (worker_score *)Copy_pool(pool, ptr,
                                                   image->thread_limit * sizeof(worker_score), char);
        ptr += image->thread_limit * sizeof(worker_score);
    }

    sb->global  = (global_score *)Copy_pool(pool, ptr,
                                            sizeof(global_score), char);

    image->pool = pool;
    image->sb   = sb;

   /* debug_dump_sb(image); */

    RETVAL = image;

    OUTPUT:
    RETVAL


SV *
image(CLASS, pool_sv)
    SV *CLASS
    SV *pool_sv
    
    INIT:
    modperl_scoreboard_t *image;
    apr_pool_t *pool = mp_xs_sv2_APR__Pool(pool_sv);

    CODE:
    image_sanity_check(aTHX);

    image = (modperl_scoreboard_t *)apr_palloc(pool, sizeof(*image));

    if (ap_exists_scoreboard_image()) {
        image->sb   = ap_scoreboard_image;
        image->pool = pool;
        ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &(image->server_limit));
        ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &(image->thread_limit));
    }
    else {
        Perl_croak(aTHX_ "ap_scoreboard_image doesn't exist");
    }
    RETVAL = sv_setref_pv(NEWSV(0, 0), "Apache::Scoreboard", (void*)image);
    /* make sure the pool sticks around as long as this object is alive */
    mpxs_add_pool_magic(RETVAL, pool_sv);

    CLASS = CLASS; /* avoid warnings */

    OUTPUT:
    RETVAL


int
server_limit(image)
    Apache::Scoreboard image

int
thread_limit(image)
    Apache::Scoreboard image

    
Apache::ScoreboardParentScore
parent_score(image, idx=0)
    Apache::Scoreboard image
    int idx

    PREINIT:
    process_score *ps;
    
    CODE:
    ps = my_get_scoreboard_process(aTHX_ image, idx);
    /* XXX */
    if (!ps->quiescing && ps->pid) {
        RETVAL = (modperl_parent_score_t *)apr_pcalloc(image->pool,
                                                       (sizeof(*RETVAL)));
        RETVAL->record = ps;
        RETVAL->idx    = idx;
        RETVAL->image  = image;
    }
    else {
        XSRETURN_UNDEF;
    }

    OUTPUT:
    RETVAL

Apache::ScoreboardWorkerScore
worker_score(image, parent_idx, worker_idx)
    Apache::Scoreboard image
    int parent_idx
    int worker_idx

    PREINIT:
    worker_score *ws;
    
    CODE:
    ws = my_get_scoreboard_worker(aTHX_ image, parent_idx, worker_idx);
    RETVAL = (modperl_worker_score_t *)apr_pcalloc(image->pool,
                                                   (sizeof(*RETVAL)));
    RETVAL->parent_idx = parent_idx;
    RETVAL->worker_idx = worker_idx;
    RETVAL->record = ws;
    
    OUTPUT:
    RETVAL

SV *
pids(image)
    Apache::Scoreboard image

    PREINIT:
    AV *av = newAV();
    int i;
    scoreboard *sb;

    CODE:
    sb = image->sb;
    for (i = 0; i < image->server_limit; i++) {
        if (!(sb->parent[i].pid)) {
            break;
        }
        /* fprintf(stderr, "pids: server %d: pid %d\n",
           i, (int)(sb->parent[i].pid)); */
        av_push(av, newSViv(sb->parent[i].pid));
    }
        
    RETVAL = newRV_noinc((SV*)av);

    OUTPUT:
    RETVAL

# XXX: need to move pid_t => apr_proc_t and work with pid->pid as in
# find_child_by_pid from scoreboard.c

int
parent_idx_by_pid(image, pid)   
    Apache::Scoreboard image
    pid_t pid

    PREINIT:
    int i;
    scoreboard *sb;

    CODE:
    sb = image->sb;
    RETVAL = -1;

    for (i = 0; i < image->server_limit; i++) {
        if (sb->parent[i].pid == pid) {
            RETVAL = i;
            break;
        }
    }

    OUTPUT:
    RETVAL

SV *
thread_numbers(image, parent_idx)
    Apache::Scoreboard image
    int parent_idx

    PREINIT:
    AV *av = newAV();
    int i;
    scoreboard *sb;

    CODE:
    sb = image->sb;

    for (i = 0; i < image->thread_limit; ++i) {
        /* fprintf(stderr, "thread_num: server %d, thread %d pid %d\n",
           i, sb->servers[parent_idx][i].thread_num,
           (int)(sb->parent[parent_idx].pid)); */
        
        av_push(av, newSViv(sb->servers[parent_idx][i].thread_num));
    }

    RETVAL = newRV_noinc((SV*)av);

    OUTPUT:
    RETVAL

apr_uint32_t
scoreboard_up_time(image)
    Apache::Scoreboard image








MODULE = Apache::Scoreboard PACKAGE = Apache::ScoreboardParentScore PREFIX = parent_score_
    
Apache::ScoreboardParentScore
next(self)
    Apache::ScoreboardParentScore self

    PREINIT:
    int next_idx;
    process_score *ps;
    modperl_scoreboard_t *image;

    CODE:
    image = self->image;
    next_idx = self->idx + 1;
    if (next_idx <= image->server_limit) {
        ps = my_get_scoreboard_process(aTHX_ image, next_idx);
    }
    else {
        XSRETURN_UNDEF;
    }

    if (ps->pid) {
        RETVAL = (modperl_parent_score_t *)apr_pcalloc(image->pool,
                                                       sizeof(*RETVAL));
        RETVAL->record = ps;
        RETVAL->idx    = next_idx;
        RETVAL->image  = image;
    }
    else {
        XSRETURN_UNDEF;
    }

    OUTPUT:
    RETVAL

Apache::ScoreboardWorkerScore
worker_score(self)
    Apache::ScoreboardParentScore self

    CODE:
    RETVAL = (modperl_worker_score_t *)apr_pcalloc(self->image->pool,
                                                   sizeof(*RETVAL));
    RETVAL->parent_idx = self->idx;
    RETVAL->worker_idx = 0;
    RETVAL->record     = my_get_scoreboard_worker(aTHX_ self->image,
                                                  self->idx, 0);

    OUTPUT:
    RETVAL
    
Apache::ScoreboardWorkerScore
next_worker_score(self, mws)
    Apache::ScoreboardParentScore self
    Apache::ScoreboardWorkerScore mws

    PREINIT:
    int next_idx;
    
    CODE:
    next_idx = mws->worker_idx + 1;
    if (next_idx < self->image->thread_limit) {
        RETVAL = (modperl_worker_score_t *)apr_pcalloc(self->image->pool,
                                                       sizeof(*RETVAL));
        RETVAL->parent_idx = mws->parent_idx;
        RETVAL->worker_idx = next_idx;
        RETVAL->record     = my_get_scoreboard_worker(aTHX_ self->image,
                                                      mws->parent_idx, next_idx);
    }
    else {
        XSRETURN_UNDEF;
    }

    OUTPUT:
    RETVAL
    
    
Apache::ScoreboardWorkerScore
next_live_worker_score(self, mws)
    Apache::ScoreboardParentScore self
    Apache::ScoreboardWorkerScore mws

    PREINIT:
    int next_idx;
    int found = 0;
    
    CODE:
    next_idx = mws->worker_idx;

    while (++next_idx < self->image->thread_limit) {
        worker_score *ws = my_get_scoreboard_worker(aTHX_ self->image,
                                                    mws->parent_idx, next_idx);
        if (LIVE_WORKER(ws)) {
            RETVAL = (modperl_worker_score_t *)apr_pcalloc(self->image->pool,
                                                           sizeof(*RETVAL));
            RETVAL->record     = ws;
            RETVAL->parent_idx = mws->parent_idx;
            RETVAL->worker_idx = next_idx;
            found++;
            break;
        }
    }

    if (!found) {
        XSRETURN_UNDEF;
    }

    OUTPUT:
    RETVAL
    


Apache::ScoreboardWorkerScore
next_active_worker_score(self, mws)
    Apache::ScoreboardParentScore self
    Apache::ScoreboardWorkerScore mws

    PREINIT:
    int next_idx;
    int found = 0;

    CODE:
    next_idx = mws->worker_idx;
    while (++next_idx < self->image->thread_limit) {
        worker_score *ws = my_get_scoreboard_worker(aTHX_ self->image,
                                                    mws->parent_idx, next_idx);
        if (ACTIVE_WORKER(ws)) {
            RETVAL = (modperl_worker_score_t *)apr_pcalloc(self->image->pool,
                                                           sizeof(*RETVAL));
            RETVAL->record     = ws;
            RETVAL->parent_idx = mws->parent_idx;
            RETVAL->worker_idx = next_idx;
            found++;
            break;
        }
    }

    if (!found) {
        XSRETURN_UNDEF;
    }

    OUTPUT:
    RETVAL

pid_t
parent_score_pid(self)
    Apache::ScoreboardParentScore self









MODULE = Apache::Scoreboard PACKAGE = Apache::ScoreboardWorkerScore PREFIX = worker_score_

void
times(self)
    Apache::ScoreboardWorkerScore self

    PPCODE:
    if (GIMME == G_ARRAY) {
        /* same return values as CORE::times() */
        EXTEND(sp, 4);
        PUSHs(sv_2mortal(newSViv(self->record->times.tms_utime)));
        PUSHs(sv_2mortal(newSViv(self->record->times.tms_stime)));
        PUSHs(sv_2mortal(newSViv(self->record->times.tms_cutime)));
        PUSHs(sv_2mortal(newSViv(self->record->times.tms_cstime)));
    }
    else {
#ifdef _SC_CLK_TCK
        float tick = sysconf(_SC_CLK_TCK);
#else
        float tick = HZ;
#endif
        if (self->record->access_count) {
            /* cpu %, same value mod_status displays */
            float RETVAL = (self->record->times.tms_utime +
                            self->record->times.tms_stime +
                            self->record->times.tms_cutime +
                            self->record->times.tms_cstime);
            XPUSHs(sv_2mortal(newSVnv((double)RETVAL/tick)));

Scoreboard.xs  view on Meta::CPAN

                 self->record->start_time,
                 self->record->stop_time,
                 self->record->last_used);
    }

    /* do the same as Time::HiRes::gettimeofday */
    if (GIMME == G_ARRAY) {
        EXTEND(sp, 2);
        PUSHs(sv_2mortal(newSViv(apr_time_sec(tp))));
        PUSHs(sv_2mortal(newSViv(apr_time_usec(tp))));
    } 
    else {
        EXTEND(sp, 1);
        PUSHs(sv_2mortal(newSVnv(apr_time_sec(tp))));
    }

long
req_time(self)
    Apache::ScoreboardWorkerScore self

    CODE:
    if (self->record->start_time == 0L) {
        RETVAL = 0L;
    }
    else {
        RETVAL = (long)
            ((self->record->stop_time - self->record->start_time) / 1000);
    }
    if (RETVAL < 0L || !self->record->access_count) {
        RETVAL = 0L;
    }

    OUTPUT:
    RETVAL

SV *
worker_score_status(self)
    Apache::ScoreboardWorkerScore self

    CODE:
    RETVAL = newSV(0);
    sv_setnv(RETVAL, (double)self->record->status);
    Perl_sv_setpvf(aTHX_ RETVAL, "%c", status_flags[self->record->status]);
    SvNOK_on(RETVAL); /* dual-var */ 

    OUTPUT:
    RETVAL

# at the moment always gives 0 (blame httpd)    
U32
worker_score_tid(self)
    Apache::ScoreboardWorkerScore self

    CODE:
    RETVAL = worker_score_tid(self);

    OUTPUT:
    RETVAL
    
int
worker_score_thread_num(self)
    Apache::ScoreboardWorkerScore self
    
unsigned long
worker_score_access_count(self)
    Apache::ScoreboardWorkerScore self

unsigned long
worker_score_bytes_served(self)
    Apache::ScoreboardWorkerScore self

unsigned long
worker_score_my_access_count(self)
    Apache::ScoreboardWorkerScore self

unsigned long
worker_score_my_bytes_served(self)
    Apache::ScoreboardWorkerScore self

unsigned long
worker_score_conn_bytes(self)
    Apache::ScoreboardWorkerScore self

unsigned short
worker_score_conn_count(self)
    Apache::ScoreboardWorkerScore self

char *
worker_score_client(self)
    Apache::ScoreboardWorkerScore self

char *
worker_score_request(self)
    Apache::ScoreboardWorkerScore self

char *
worker_score_vhost(self)
    Apache::ScoreboardWorkerScore self

apr_uint32_t
worker_score_most_recent(self)
    Apache::ScoreboardWorkerScore self



( run in 0.588 second using v1.01-cache-2.11-cpan-39bf76dae61 )