/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- sampler_flush
- sampler_sample
- collect_samples
- Scm_ProfilerCountBufferFlush
- Scm_ProfilerStart
- Scm_ProfilerStop
- Scm_ProfilerReset
- Scm_ProfilerRawResult
- Scm_ProfilerStart
- Scm_ProfilerStop
- Scm_ProfilerReset
- 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 */