1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36: #define LIBGAUCHE_BODY
37: #include "gauche.h"
38: #include "gauche/vm.h"
39: #include "gauche/code.h"
40: #include "gauche/vminsn.h"
41: #include "gauche/prof.h"
42:
43: #ifdef GAUCHE_PROFILE
44:
45:
46: #ifdef GAUCHE_USE_PTHREADS
47: #define SIGPROCMASK pthread_sigmask
48: #else
49: #define SIGPROCMASK sigprocmask
50: #endif
51:
52:
53:
54:
55:
56: #define SAMPLING_PERIOD 10000
57:
58: #define ITIMER_START() \
59: do { \
60: struct itimerval tval, oval; \
61: tval.it_interval.tv_sec = 0; \
62: tval.it_interval.tv_usec = SAMPLING_PERIOD; \
63: tval.it_value.tv_sec = 0; \
64: tval.it_value.tv_usec = SAMPLING_PERIOD; \
65: setitimer(ITIMER_PROF, &tval, &oval); \
66: } while (0)
67:
68: #define ITIMER_STOP() \
69: do { \
70: struct itimerval tval, oval; \
71: tval.it_interval.tv_sec = 0; \
72: tval.it_interval.tv_usec = 0; \
73: tval.it_value.tv_sec = 0; \
74: tval.it_value.tv_usec = 0; \
75: setitimer(ITIMER_PROF, &tval, &oval); \
76: } while (0)
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87: #define CHK(exp) do { if (!(exp)) goto bad; } while (0)
88:
89: static void sampler_flush(ScmVM *vm)
90: {
91: int nsamples;
92: ssize_t r;
93:
94: if (vm->prof == NULL) return;
95: if (vm->prof->samplerFd < 0 || vm->prof->currentSample == 0) return;
96:
97: nsamples = vm->prof->currentSample;
98: r = write(vm->prof->samplerFd, vm->prof->samples,
99: nsamples * sizeof(ScmProfSample[1]));
100: if (r == (ssize_t)-1) {
101: vm->prof->errorOccurred++;
102: }
103: vm->prof->currentSample = 0;
104: return;
105: }
106:
107:
108: static void sampler_sample(int sig)
109: {
110: ScmVM *vm;
111: int i;
112:
113: vm = Scm_VM();
114: if (vm->prof == NULL) return;
115: if (vm->prof->state != SCM_PROFILER_RUNNING) return;
116:
117: if (vm->prof->currentSample >= SCM_PROF_SAMPLES_IN_BUFFER) {
118: ITIMER_STOP();
119: sampler_flush(vm);
120: ITIMER_START();
121: }
122:
123: i = vm->prof->currentSample++;
124: if (vm->base) {
125:
126:
127: if (vm->pc && SCM_VM_INSN_CODE(*vm->pc) == SCM_VM_RET
128: && SCM_SUBRP(vm->val0)) {
129: vm->prof->samples[i].func = vm->val0;
130: vm->prof->samples[i].pc = NULL;
131: } else {
132: vm->prof->samples[i].func = SCM_OBJ(vm->base);
133: vm->prof->samples[i].pc = vm->pc;
134: }
135: } else {
136: vm->prof->samples[i].func = SCM_FALSE;
137: vm->prof->samples[i].pc = NULL;
138: }
139: vm->prof->totalSamples++;
140: }
141:
142:
143: void collect_samples(ScmVMProfiler *prof)
144: {
145: int i, cnt;
146: for (i=0; i<prof->currentSample; i++) {
147: ScmHashEntry *e = Scm_HashTableGet(prof->statHash,
148: prof->samples[i].func);
149: if (e == NULL) {
150:
151: Scm_Warn("profiler: uncounted object appeared in a sample: %p (%S)\n",
152: prof->samples[i].func, prof->samples[i].func);
153: } else {
154: SCM_ASSERT(SCM_PAIRP(e->value));
155: cnt = SCM_INT_VALUE(SCM_CDR(e->value)) + 1;
156: SCM_SET_CDR(e->value, SCM_MAKE_INT(cnt));
157: }
158: }
159: }
160:
161:
162:
163:
164:
165:
166:
167:
168: void Scm_ProfilerCountBufferFlush(ScmVM *vm)
169: {
170: int i, ncounts;
171: ScmObj func;
172: sigset_t set;
173:
174: if (vm->prof == NULL) return;
175: if (vm->prof->currentCount == 0) return;
176:
177:
178: sigemptyset(&set);
179: sigaddset(&set, SIGPROF);
180: SIGPROCMASK(SIG_BLOCK, &set, NULL);
181:
182: ncounts = vm->prof->currentCount;
183: for (i=0; i<ncounts; i++) {
184: ScmHashEntry *e;
185: int cnt;
186:
187: func = vm->prof->counts[i].func;
188: if (SCM_METHODP(func) && SCM_METHOD(func)->func == NULL) {
189:
190:
191:
192: func = SCM_OBJ(SCM_METHOD(func)->data);
193: }
194:
195: e = Scm_HashTableAdd(vm->prof->statHash,
196: vm->prof->counts[i].func,
197: SCM_FALSE);
198: if (SCM_FALSEP(e->value)) {
199: e->value = Scm_Cons(SCM_MAKE_INT(0), SCM_MAKE_INT(0));
200: }
201:
202: SCM_ASSERT(SCM_PAIRP(e->value));
203: cnt = SCM_INT_VALUE(SCM_CAR(e->value)) + 1;
204: SCM_SET_CAR(e->value, SCM_MAKE_INT(cnt));
205: }
206: vm->prof->currentCount = 0;
207:
208:
209: SIGPROCMASK(SIG_UNBLOCK, &set, NULL);
210: }
211:
212:
213:
214:
215: void Scm_ProfilerStart(void)
216: {
217: struct sigaction act;
218: ScmVM *vm = Scm_VM();
219: char templat[] = "/tmp/gauche-profXXXXXX";
220:
221: if (!vm->prof) {
222: vm->prof = SCM_NEW(ScmVMProfiler);
223: vm->prof->state = SCM_PROFILER_INACTIVE;
224: vm->prof->samplerFd = Scm_Mkstemp(templat);
225: vm->prof->currentSample = 0;
226: vm->prof->totalSamples = 0;
227: vm->prof->errorOccurred = 0;
228: vm->prof->currentCount = 0;
229: vm->prof->statHash =
230: SCM_HASH_TABLE(Scm_MakeHashTableSimple(SCM_HASH_EQ, 0));
231: unlink(templat);
232: } else if (vm->prof->samplerFd < 0) {
233: vm->prof->samplerFd = Scm_Mkstemp(templat);
234: unlink(templat);
235: }
236:
237: if (vm->prof->state == SCM_PROFILER_RUNNING) return;
238: vm->prof->state = SCM_PROFILER_RUNNING;
239: vm->profilerRunning = TRUE;
240:
241:
242: act.sa_handler = sampler_sample;
243: sigfillset(&act.sa_mask);
244: act.sa_flags = SA_RESTART;
245: if (sigaction(SIGPROF, &act, NULL) < 0) {
246: Scm_SysError("sigaction failed");
247: }
248:
249: ITIMER_START();
250: }
251:
252: int Scm_ProfilerStop(void)
253: {
254: ScmVM *vm = Scm_VM();
255: if (vm->prof == NULL) return 0;
256: if (vm->prof->state != SCM_PROFILER_RUNNING) return 0;
257: ITIMER_STOP();
258: vm->prof->state = SCM_PROFILER_PAUSING;
259: vm->profilerRunning = FALSE;
260: return vm->prof->totalSamples;
261: }
262:
263: void Scm_ProfilerReset(void)
264: {
265: ScmVM *vm = Scm_VM();
266:
267: if (vm->prof == NULL) return;
268: if (vm->prof->state == SCM_PROFILER_INACTIVE) return;
269: if (vm->prof->state == SCM_PROFILER_RUNNING) Scm_ProfilerStop();
270:
271: if (vm->prof->samplerFd >= 0) {
272: close(vm->prof->samplerFd);
273: vm->prof->samplerFd = -1;
274: }
275: vm->prof->totalSamples = 0;
276: vm->prof->currentSample = 0;
277: vm->prof->errorOccurred = 0;
278: vm->prof->currentCount = 0;
279: vm->prof->statHash =
280: SCM_HASH_TABLE(Scm_MakeHashTableSimple(SCM_HASH_EQ, 0));
281: vm->prof->state = SCM_PROFILER_INACTIVE;
282: }
283:
284:
285: ScmObj Scm_ProfilerRawResult(void)
286: {
287: off_t off;
288: ssize_t r;
289: ScmObj sampler_port;
290: ScmVM *vm = Scm_VM();
291:
292: if (vm->prof == NULL) return SCM_FALSE;
293: if (vm->prof->state == SCM_PROFILER_INACTIVE) return SCM_FALSE;
294: if (vm->prof->state == SCM_PROFILER_RUNNING) Scm_ProfilerStop();
295:
296: if (vm->prof->errorOccurred > 0) {
297: Scm_Warn("profiler: An error has been occurred during saving profiling samples. The result may not be accurate");
298: }
299:
300: Scm_ProfilerCountBufferFlush(vm);
301:
302:
303: collect_samples(vm->prof);
304:
305:
306: SCM_SYSCALL(off, lseek(vm->prof->samplerFd, 0, SEEK_SET));
307: if (off == (off_t)-1) {
308: Scm_ProfilerReset();
309: Scm_Error("profiler: seek failed in retrieving sample data");
310: }
311: sampler_port =
312: Scm_MakePortWithFd(SCM_FALSE, SCM_PORT_INPUT, vm->prof->samplerFd,
313: SCM_PORT_BUFFER_FULL, FALSE);
314:
315: for (;;) {
316: r = read(vm->prof->samplerFd, vm->prof->samples,
317: sizeof(ScmProfSample[1]) * SCM_PROF_SAMPLES_IN_BUFFER);
318: if (r <= 0) break;
319: vm->prof->currentSample = r / sizeof(ScmProfSample[1]);
320: collect_samples(vm->prof);
321: }
322: vm->prof->currentSample = 0;
323: if (ftruncate(vm->prof->samplerFd, 0) < 0) {
324: Scm_SysError("profiler: failed to truncate temporary file");
325: }
326:
327: return SCM_OBJ(vm->prof->statHash);
328: }
329:
330: #else
331: void Scm_ProfilerStart(void)
332: {
333: Scm_Error("profiler is not supported.");
334: }
335:
336: int Scm_ProfilerStop(void)
337: {
338: Scm_Error("profiler is not supported.");
339: return 0;
340: }
341:
342: void Scm_ProfilerReset(void)
343: {
344: Scm_Error("profiler is not supported.");
345: }
346:
347: ScmObj Scm_ProfilerRawResult(void)
348: {
349: Scm_Error("profiler is not supported.");
350: return SCM_FALSE;
351: }
352: #endif