root/src/prof.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sampler_flush
  2. sampler_sample
  3. collect_samples
  4. Scm_ProfilerCountBufferFlush
  5. Scm_ProfilerStart
  6. Scm_ProfilerStop
  7. Scm_ProfilerReset
  8. Scm_ProfilerRawResult
  9. Scm_ProfilerStart
  10. Scm_ProfilerStop
  11. Scm_ProfilerReset
  12. Scm_ProfilerRawResult

   1 /*
   2  * prof.c - profiler
   3  *
   4  *   Copyright (c) 2005 Shiro Kawai, All rights reserved.
   5  * 
   6  *   Redistribution and use in source and binary forms, with or without
   7  *   modification, are permitted provided that the following conditions
   8  *   are met:
   9  * 
  10  *   1. Redistributions of source code must retain the above copyright
  11  *      notice, this list of conditions and the following disclaimer.
  12  *
  13  *   2. Redistributions in binary form must reproduce the above copyright
  14  *      notice, this list of conditions and the following disclaimer in the
  15  *      documentation and/or other materials provided with the distribution.
  16  *
  17  *   3. Neither the name of the authors nor the names of its contributors
  18  *      may be used to endorse or promote products derived from this
  19  *      software without specific prior written permission.
  20  *
  21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  27  *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  28  *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  29  *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  30  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  31  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  *
  33  *  $Id: prof.c,v 1.8 2005/10/03 01:02:39 shirok Exp $
  34  */
  35 
  36 #include <stdlib.h>
  37 #include <unistd.h>
  38 #define LIBGAUCHE_BODY
  39 #include "gauche.h"
  40 #include "gauche/vm.h"
  41 #include "gauche/code.h"
  42 #include "gauche/vminsn.h"
  43 #include "gauche/prof.h"
  44 
  45 #ifdef GAUCHE_PROFILE
  46 
  47 /* WARNING: duplicated code - see signal.c; we should integrate them later */
  48 #ifdef GAUCHE_USE_PTHREADS
  49 #define SIGPROCMASK pthread_sigmask
  50 #else
  51 #define SIGPROCMASK sigprocmask
  52 #endif
  53 
  54 /*=============================================================
  55  * Interval timer operation
  56  */
  57 
  58 #define SAMPLING_PERIOD 10000
  59 
  60 #define ITIMER_START()                                  \
  61     do {                                                \
  62         struct itimerval tval, oval;                    \
  63         tval.it_interval.tv_sec = 0;                    \
  64         tval.it_interval.tv_usec = SAMPLING_PERIOD;     \
  65         tval.it_value.tv_sec = 0;                       \
  66         tval.it_value.tv_usec = SAMPLING_PERIOD;        \
  67         setitimer(ITIMER_PROF, &tval, &oval);           \
  68     } while (0)
  69 
  70 #define ITIMER_STOP()                           \
  71     do {                                        \
  72         struct itimerval tval, oval;            \
  73         tval.it_interval.tv_sec = 0;            \
  74         tval.it_interval.tv_usec = 0;           \
  75         tval.it_value.tv_sec = 0;               \
  76         tval.it_value.tv_usec = 0;              \
  77         setitimer(ITIMER_PROF, &tval, &oval);   \
  78     } while (0)
  79 
  80 /*=============================================================
  81  * Statistic sampler
  82  */
  83 
  84 /* Flush sample buffer to the file.
  85    We save the address value to the file.  The address should also be
  86    recorded in the call counter, thus we don't need to worry about
  87    the addressed object being GCed. */
  88 
  89 #define CHK(exp)  do { if (!(exp)) goto bad; } while (0)
  90 
  91 static void sampler_flush(ScmVM *vm)
  92 {
  93     int nsamples;
  94     ssize_t r;
  95     
  96     if (vm->prof == NULL) return; /* for safety */
  97     if (vm->prof->samplerFd < 0 || vm->prof->currentSample == 0) return;
  98 
  99     nsamples = vm->prof->currentSample;
 100     r = write(vm->prof->samplerFd, vm->prof->samples,
 101               nsamples * sizeof(ScmProfSample[1]));
 102     if (r == (ssize_t)-1) {
 103         vm->prof->errorOccurred++;
 104     }
 105     vm->prof->currentSample = 0;
 106     return;
 107 }
 108 
 109 /* signal handler */
 110 static void sampler_sample(int sig)
 111 {
 112     ScmVM *vm;
 113     int i;
 114 
 115     vm = Scm_VM();
 116     if (vm->prof == NULL) return;
 117     if (vm->prof->state != SCM_PROFILER_RUNNING) return;
 118 
 119     if (vm->prof->currentSample >= SCM_PROF_SAMPLES_IN_BUFFER) {
 120         ITIMER_STOP();
 121         sampler_flush(vm);
 122         ITIMER_START();
 123     }
 124 
 125     i = vm->prof->currentSample++;
 126     if (vm->base) {
 127         /* If vm->pc is RET and val0 is a subr, it is pretty likely that
 128            we're actually executing that subr. */
 129         if (vm->pc && SCM_VM_INSN_CODE(*vm->pc) == SCM_VM_RET
 130             && SCM_SUBRP(vm->val0)) {
 131             vm->prof->samples[i].func = vm->val0;
 132             vm->prof->samples[i].pc = NULL;
 133         } else {
 134             vm->prof->samples[i].func = SCM_OBJ(vm->base);
 135             vm->prof->samples[i].pc = vm->pc;
 136         }
 137     } else {
 138         vm->prof->samples[i].func = SCM_FALSE;
 139         vm->prof->samples[i].pc = NULL;
 140     }
 141     vm->prof->totalSamples++;
 142 }
 143 
 144 /* register samples into the stat table.  Called from Scm_ProfilerResult */
 145 void collect_samples(ScmVMProfiler *prof)
 146 {
 147     int i, cnt;
 148     for (i=0; i<prof->currentSample; i++) {
 149         ScmHashEntry *e = Scm_HashTableGet(prof->statHash,
 150                                            prof->samples[i].func);
 151         if (e == NULL) {
 152             /* NB: just for now */
 153             Scm_Warn("profiler: uncounted object appeared in a sample: %p (%S)\n",
 154                      prof->samples[i].func, prof->samples[i].func);
 155         } else {
 156             SCM_ASSERT(SCM_PAIRP(e->value));
 157             cnt = SCM_INT_VALUE(SCM_CDR(e->value)) + 1;
 158             SCM_SET_CDR(e->value, SCM_MAKE_INT(cnt));
 159         }
 160     }
 161 }
 162 
 163 /*=============================================================
 164  * Call Counter
 165  */
 166 
 167 /* Inserting data into array is done in a macro (prof.h).  It calls
 168    this flush routine when the array gets full. */
 169 
 170 void Scm_ProfilerCountBufferFlush(ScmVM *vm)
 171 {
 172     int i, ncounts;
 173     ScmObj func;
 174     sigset_t set;
 175 
 176     if (vm->prof == NULL) return; /* for safety */
 177     if (vm->prof->currentCount == 0) return;
 178 
 179     /* suspend itimer during hash table operation */
 180     sigemptyset(&set);
 181     sigaddset(&set, SIGPROF);
 182     SIGPROCMASK(SIG_BLOCK, &set, NULL);
 183 
 184     ncounts = vm->prof->currentCount;
 185     for (i=0; i<ncounts; i++) {
 186         ScmHashEntry *e;
 187         int cnt;
 188         
 189         func = vm->prof->counts[i].func;
 190         if (SCM_METHODP(func) && SCM_METHOD(func)->func == NULL) {
 191             /* func is Scheme-defined method.  Record the code of
 192                method body, so that we can match it with sampling
 193                profiler later. */
 194             func = SCM_OBJ(SCM_METHOD(func)->data);
 195         }
 196         
 197         e = Scm_HashTableAdd(vm->prof->statHash,
 198                              vm->prof->counts[i].func,
 199                              SCM_FALSE);
 200         if (SCM_FALSEP(e->value)) {
 201             e->value = Scm_Cons(SCM_MAKE_INT(0), SCM_MAKE_INT(0));
 202         }
 203 
 204         SCM_ASSERT(SCM_PAIRP(e->value));
 205         cnt = SCM_INT_VALUE(SCM_CAR(e->value)) + 1;
 206         SCM_SET_CAR(e->value, SCM_MAKE_INT(cnt));
 207     }
 208     vm->prof->currentCount = 0;
 209 
 210     /* resume itimer */
 211     SIGPROCMASK(SIG_UNBLOCK, &set, NULL);
 212 }
 213 
 214 /*=============================================================
 215  * External API
 216  */
 217 void Scm_ProfilerStart(void)
 218 {
 219     struct sigaction act;
 220     ScmVM *vm = Scm_VM();
 221     char templat[] = "/tmp/gauche-profXXXXXX";
 222 
 223     if (!vm->prof) {
 224         vm->prof = SCM_NEW(ScmVMProfiler);
 225         vm->prof->state = SCM_PROFILER_INACTIVE;
 226         vm->prof->samplerFd = Scm_Mkstemp(templat);
 227         vm->prof->currentSample = 0;
 228         vm->prof->totalSamples = 0;
 229         vm->prof->errorOccurred = 0;
 230         vm->prof->currentCount = 0;
 231         vm->prof->statHash =
 232             SCM_HASH_TABLE(Scm_MakeHashTableSimple(SCM_HASH_EQ, 0));
 233         unlink(templat);       /* keep anonymous tmpfile */
 234     } else if (vm->prof->samplerFd < 0) {
 235         vm->prof->samplerFd = Scm_Mkstemp(templat);
 236         unlink(templat);
 237     }
 238     
 239     if (vm->prof->state == SCM_PROFILER_RUNNING) return;
 240     vm->prof->state = SCM_PROFILER_RUNNING;
 241     vm->profilerRunning = TRUE;
 242 
 243     /* NB: this should be done globally!!! */
 244     act.sa_handler = sampler_sample;
 245     sigfillset(&act.sa_mask);
 246     act.sa_flags = SA_RESTART;
 247     if (sigaction(SIGPROF, &act, NULL) < 0) {
 248         Scm_SysError("sigaction failed");
 249     }
 250 
 251     ITIMER_START();
 252 }
 253 
 254 int Scm_ProfilerStop(void)
 255 {
 256     ScmVM *vm = Scm_VM();
 257     if (vm->prof == NULL) return 0;
 258     if (vm->prof->state != SCM_PROFILER_RUNNING) return 0;
 259     ITIMER_STOP();
 260     vm->prof->state = SCM_PROFILER_PAUSING;
 261     vm->profilerRunning = FALSE;
 262     return vm->prof->totalSamples;
 263 }
 264 
 265 void Scm_ProfilerReset(void)
 266 {
 267     ScmVM *vm = Scm_VM();
 268     
 269     if (vm->prof == NULL) return;
 270     if (vm->prof->state == SCM_PROFILER_INACTIVE) return;
 271     if (vm->prof->state == SCM_PROFILER_RUNNING) Scm_ProfilerStop();
 272 
 273     if (vm->prof->samplerFd >= 0) {
 274         close(vm->prof->samplerFd);
 275         vm->prof->samplerFd = -1;
 276     }
 277     vm->prof->totalSamples = 0;
 278     vm->prof->currentSample = 0;
 279     vm->prof->errorOccurred = 0;
 280     vm->prof->currentCount = 0;
 281     vm->prof->statHash =
 282         SCM_HASH_TABLE(Scm_MakeHashTableSimple(SCM_HASH_EQ, 0));
 283     vm->prof->state = SCM_PROFILER_INACTIVE;
 284 }
 285 
 286 /* Returns the statHash */
 287 ScmObj Scm_ProfilerRawResult(void)
 288 {
 289     off_t off;
 290     ssize_t r;
 291     ScmObj sampler_port;
 292     ScmVM *vm = Scm_VM();
 293 
 294     if (vm->prof == NULL) return SCM_FALSE;
 295     if (vm->prof->state == SCM_PROFILER_INACTIVE) return SCM_FALSE;
 296     if (vm->prof->state == SCM_PROFILER_RUNNING) Scm_ProfilerStop();
 297 
 298     if (vm->prof->errorOccurred > 0) {
 299         Scm_Warn("profiler: An error has been occurred during saving profiling samples.  The result may not be accurate");
 300     }
 301 
 302     Scm_ProfilerCountBufferFlush(vm);
 303 
 304     /* collect samples in the current buffer */
 305     collect_samples(vm->prof);
 306 
 307     /* collect samples in the saved file */
 308     SCM_SYSCALL(off, lseek(vm->prof->samplerFd, 0, SEEK_SET));
 309     if (off == (off_t)-1) {
 310         Scm_ProfilerReset();
 311         Scm_Error("profiler: seek failed in retrieving sample data");
 312     }
 313     sampler_port =
 314         Scm_MakePortWithFd(SCM_FALSE, SCM_PORT_INPUT, vm->prof->samplerFd,
 315                            SCM_PORT_BUFFER_FULL, FALSE);
 316 
 317     for (;;) {
 318         r = read(vm->prof->samplerFd, vm->prof->samples,
 319                  sizeof(ScmProfSample[1]) * SCM_PROF_SAMPLES_IN_BUFFER);
 320         if (r <= 0) break;
 321         vm->prof->currentSample = r / sizeof(ScmProfSample[1]);
 322         collect_samples(vm->prof);
 323     }
 324     vm->prof->currentSample = 0;
 325     if (ftruncate(vm->prof->samplerFd, 0) < 0) {
 326         Scm_SysError("profiler: failed to truncate temporary file");
 327     }
 328     
 329     return SCM_OBJ(vm->prof->statHash);
 330 }
 331 
 332 #else  /* !GAUCHE_PROFILE */
 333 void Scm_ProfilerStart(void)
 334 {
 335     Scm_Error("profiler is not supported.");
 336 }
 337 
 338 int  Scm_ProfilerStop(void)
 339 {
 340     Scm_Error("profiler is not supported.");
 341 }
 342 
 343 void Scm_ProfilerReset(void)
 344 {
 345     Scm_Error("profiler is not supported.");
 346 }
 347 
 348 ScmObj Scm_ProfilerRawResult(void)
 349 {
 350     Scm_Error("profiler is not supported.");
 351     return SCM_FALSE;
 352 }
 353 #endif /* !GAUCHE_PROFILE */

/* [<][>][^][v][top][bottom][index][help] */