Try to let it compile on other platforms
[bpt/emacs.git] / src / profiler.c
CommitLineData
0efc778b 1/* Profiler implementation.
c2d7786e
TM
2
3Copyright (C) 2012 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19
20#include <config.h>
21#include <stdio.h>
22#include <limits.h>
23#include <sys/time.h>
24#include <signal.h>
25#include <setjmp.h>
26#include "lisp.h"
27
0efc778b
TM
28/* True if sampling profiler is running. */
29
30bool sample_profiler_running;
31
32/* True if memory profiler is running. */
33
34bool memory_profiler_running;
35
c2d7786e 36static void sigprof_handler (int, siginfo_t *, void *);
c2d7786e
TM
37
38\f
3d80c99f 39/* Logs. */
c2d7786e 40
3d80c99f 41typedef struct Lisp_Hash_Table log_t;
c2d7786e
TM
42
43static Lisp_Object
3d80c99f
SM
44make_log (int heap_size, int max_stack_depth)
45{
46 /* We use a standard Elisp hash-table object, but we use it in
47 a special way. This is OK as long as the object is not exposed
48 to Elisp, i.e. until it is returned by *-profiler-log, after which
49 it can't be used any more. */
50 Lisp_Object log = make_hash_table (Qequal, make_number (heap_size),
51 make_float (DEFAULT_REHASH_SIZE),
52 make_float (DEFAULT_REHASH_THRESHOLD),
53 Qnil, Qnil, Qnil);
54 struct Lisp_Hash_Table *h = XHASH_TABLE (log);
55
56 /* What is special about our hash-tables is that the keys are pre-filled
57 with the vectors we'll put in them. */
58 int i = ASIZE (h->key_and_value) / 2;
59 while (0 < i)
60 set_hash_key_slot (h, --i,
61 Fmake_vector (make_number (max_stack_depth), Qnil));
62 return log;
c2d7786e
TM
63}
64
3d80c99f 65/* Evict the least used half of the hash_table.
c2d7786e 66
3d80c99f
SM
67 When the table is full, we have to evict someone.
68 The easiest and most efficient is to evict the value we're about to add
69 (i.e. once the table is full, stop sampling).
c2d7786e 70
3d80c99f
SM
71 We could also pick the element with the lowest count and evict it,
72 but finding it is O(N) and for that amount of work we get very
73 little in return: for the next sample, this latest sample will have
74 count==1 and will hence be a prime candidate for eviction :-(
c2d7786e 75
3d80c99f
SM
76 So instead, we take O(N) time to eliminate more or less half of the
77 entries (the half with the lowest counts). So we get an amortized
78 cost of O(1) and we get O(N) time for a new entry to grow larger
79 than the other least counts before a new round of eviction. */
c2d7786e 80
3d80c99f
SM
81static EMACS_INT approximate_median (log_t *log,
82 ptrdiff_t start, ptrdiff_t size)
c2d7786e 83{
3d80c99f
SM
84 eassert (size > 0);
85 if (size < 2)
86 return XINT (HASH_VALUE (log, start));
87 if (size < 3)
88 /* Not an actual median, but better for our application than
89 choosing either of the two numbers. */
90 return ((XINT (HASH_VALUE (log, start))
91 + XINT (HASH_VALUE (log, start + 1)))
92 / 2);
c2d7786e 93 else
c2d7786e 94 {
3d80c99f
SM
95 ptrdiff_t newsize = size / 3;
96 ptrdiff_t start2 = start + newsize;
97 EMACS_INT i1 = approximate_median (log, start, newsize);
98 EMACS_INT i2 = approximate_median (log, start2, newsize);
99 EMACS_INT i3 = approximate_median (log, start2 + newsize,
100 size - 2 * newsize);
101 return (i1 < i2
102 ? (i2 < i3 ? i2 : (i1 < i3 ? i3 : i1))
103 : (i1 < i3 ? i1 : (i2 < i3 ? i3 : i2)));
c2d7786e 104 }
c2d7786e
TM
105}
106
3d80c99f 107static void evict_lower_half (log_t *log)
c2d7786e 108{
3d80c99f
SM
109 ptrdiff_t size = ASIZE (log->key_and_value) / 2;
110 EMACS_INT median = approximate_median (log, 0, size);
111 ptrdiff_t i;
c2d7786e 112
c2d7786e 113 for (i = 0; i < size; i++)
3d80c99f
SM
114 /* Evict not only values smaller but also values equal to the median,
115 so as to make sure we evict something no matter what. */
116 if (XINT (HASH_VALUE (log, i)) <= median)
117 {
118 Lisp_Object key = HASH_KEY (log, i);
119 { /* FIXME: we could make this more efficient. */
120 Lisp_Object tmp;
121 XSET_HASH_TABLE (tmp, log); /* FIXME: Use make_lisp_ptr. */
122 Fremhash (key, tmp);
123 }
124 eassert (EQ (log->next_free, make_number (i)));
125 {
126 int j;
127 eassert (VECTORP (key));
128 for (j = 0; j < ASIZE (key); j++)
ad942b63 129 ASET (key, j, Qnil);
3d80c99f
SM
130 }
131 set_hash_key_slot (log, i, key);
132 }
c2d7786e
TM
133}
134
0efc778b
TM
135/* Record the current backtrace in LOG. BASE is a special name for
136 describing which the backtrace come from. BASE can be nil. COUNT is
137 a number how many times the profiler sees the backtrace at the
138 time. ELAPSED is a elapsed time in millisecond that the backtrace
139 took. */
140
c2d7786e 141static void
3d80c99f 142record_backtrace (log_t *log, size_t count)
c2d7786e 143{
c2d7786e 144 struct backtrace *backlist = backtrace_list;
3d80c99f
SM
145 Lisp_Object backtrace;
146 ptrdiff_t index, i = 0;
147 ptrdiff_t asize;
c2d7786e 148
3d80c99f
SM
149 if (!INTEGERP (log->next_free))
150 evict_lower_half (log);
151 index = XINT (log->next_free);
c2d7786e 152
3d80c99f
SM
153 /* Get a "working memory" vector. */
154 backtrace = HASH_KEY (log, index);
155 asize = ASIZE (backtrace);
12b3895d 156
0efc778b 157 /* Copy the backtrace contents into working memory. */
3d80c99f
SM
158 for (; i < asize && backlist; i++, backlist = backlist->next)
159 ASET (backtrace, i, *backlist->function);
0efc778b 160
3d80c99f
SM
161 /* Make sure that unused space of working memory is filled with nil. */
162 for (; i < asize; i++)
163 ASET (backtrace, i, Qnil);
c2d7786e 164
3d80c99f
SM
165 { /* We basically do a `gethash+puthash' here, except that we have to be
166 careful to avoid memory allocation since we're in a signal
167 handler, and we optimize the code to try and avoid computing the
168 hash+lookup twice. See fns.c:Fputhash for reference. */
169 EMACS_UINT hash;
170 ptrdiff_t j = hash_lookup (log, backtrace, &hash);
171 if (j >= 0)
172 set_hash_value_slot (log, j,
173 make_number (count + XINT (HASH_VALUE (log, j))));
174 else
175 { /* BEWARE! hash_put in general can allocate memory.
176 But currently it only does that if log->next_free is nil. */
177 int j;
178 eassert (!NILP (log->next_free));
179 j = hash_put (log, backtrace, make_number (count), hash);
180 /* Let's make sure we've put `backtrace' right where it
181 already was to start with. */
182 eassert (index == j);
183
184 /* FIXME: If the hash-table is almost full, we should set
185 some global flag so that some Elisp code can offload its
186 data elsewhere, so as to avoid the eviction code. */
187 }
188 }
c2d7786e 189}
c2d7786e 190\f
0efc778b 191/* Sample profiler. */
c2d7786e 192
ad942b63
SM
193#if defined SIGPROF && defined HAVE_SETITIMER
194#define PROFILER_CPU_SUPPORT
195
3d80c99f
SM
196static Lisp_Object cpu_log;
197/* Separate counter for the time spent in the GC. */
198static EMACS_INT cpu_gc_count;
0efc778b
TM
199
200/* The current sample interval in millisecond. */
201
c2d7786e
TM
202static int current_sample_interval;
203
204DEFUN ("sample-profiler-start", Fsample_profiler_start, Ssample_profiler_start,
205 1, 1, 0,
0efc778b
TM
206 doc: /* Start or restart sample profiler. Sample profiler will
207take samples each SAMPLE-INTERVAL in millisecond. See also
208`profiler-slot-heap-size' and `profiler-max-stack-depth'. */)
c2d7786e
TM
209 (Lisp_Object sample_interval)
210{
211 struct sigaction sa;
212 struct itimerval timer;
213
214 if (sample_profiler_running)
215 error ("Sample profiler is already running");
216
3d80c99f
SM
217 if (NILP (cpu_log))
218 {
219 cpu_gc_count = 0;
220 cpu_log = make_log (profiler_slot_heap_size,
221 profiler_max_stack_depth);
222 }
c2d7786e
TM
223
224 current_sample_interval = XINT (sample_interval);
225
226 sa.sa_sigaction = sigprof_handler;
227 sa.sa_flags = SA_RESTART | SA_SIGINFO;
228 sigemptyset (&sa.sa_mask);
229 sigaction (SIGPROF, &sa, 0);
230
231 timer.it_interval.tv_sec = 0;
232 timer.it_interval.tv_usec = current_sample_interval * 1000;
233 timer.it_value = timer.it_interval;
234 setitimer (ITIMER_PROF, &timer, 0);
235
236 sample_profiler_running = 1;
237
238 return Qt;
239}
240
241DEFUN ("sample-profiler-stop", Fsample_profiler_stop, Ssample_profiler_stop,
242 0, 0, 0,
0efc778b 243 doc: /* Stop sample profiler. Profiler log will be kept. */)
c2d7786e
TM
244 (void)
245{
246 if (!sample_profiler_running)
247 error ("Sample profiler is not running");
248 sample_profiler_running = 0;
249
250 setitimer (ITIMER_PROF, 0, 0);
251
252 return Qt;
253}
254
c2d7786e
TM
255DEFUN ("sample-profiler-running-p",
256 Fsample_profiler_running_p, Ssample_profiler_running_p,
257 0, 0, 0,
0efc778b 258 doc: /* Return t if sample profiler is running. */)
c2d7786e
TM
259 (void)
260{
261 return sample_profiler_running ? Qt : Qnil;
262}
263
264DEFUN ("sample-profiler-log",
265 Fsample_profiler_log, Ssample_profiler_log,
266 0, 0, 0,
0efc778b
TM
267 doc: /* Return sample profiler log. The data is a list of
268(sample nil TIMESTAMP SLOTS), where TIMESTAMP is a timestamp when the
269log is collected and SLOTS is a list of slots. */)
c2d7786e
TM
270 (void)
271{
3d80c99f
SM
272 Lisp_Object result = cpu_log;
273 /* Here we're making the log visible to Elisp , so it's not safe any
274 more for our use afterwards since we can't rely on its special
275 pre-allocated keys anymore. So we have to allocate a new one. */
276 cpu_log = (sample_profiler_running
277 ? make_log (profiler_slot_heap_size, profiler_max_stack_depth)
278 : Qnil);
279 Fputhash (Fmake_vector (make_number (1), Qautomatic_gc),
280 make_number (cpu_gc_count),
281 result);
282 cpu_gc_count = 0;
c2d7786e
TM
283 return result;
284}
ad942b63 285#endif
c2d7786e 286\f
0efc778b 287/* Memory profiler. */
c2d7786e 288
3d80c99f 289static Lisp_Object memory_log;
c2d7786e
TM
290
291DEFUN ("memory-profiler-start", Fmemory_profiler_start, Smemory_profiler_start,
292 0, 0, 0,
0efc778b
TM
293 doc: /* Start/restart memory profiler. See also
294`profiler-slot-heap-size' and `profiler-max-stack-depth'. */)
c2d7786e
TM
295 (void)
296{
297 if (memory_profiler_running)
298 error ("Memory profiler is already running");
299
3d80c99f
SM
300 if (NILP (memory_log))
301 memory_log = make_log (profiler_slot_heap_size,
c2d7786e
TM
302 profiler_max_stack_depth);
303
304 memory_profiler_running = 1;
305
306 return Qt;
307}
308
309DEFUN ("memory-profiler-stop",
310 Fmemory_profiler_stop, Smemory_profiler_stop,
311 0, 0, 0,
0efc778b 312 doc: /* Stop memory profiler. Profiler log will be kept. */)
c2d7786e
TM
313 (void)
314{
315 if (!memory_profiler_running)
316 error ("Memory profiler is not running");
317 memory_profiler_running = 0;
318
319 return Qt;
320}
321
c2d7786e
TM
322DEFUN ("memory-profiler-running-p",
323 Fmemory_profiler_running_p, Smemory_profiler_running_p,
324 0, 0, 0,
0efc778b 325 doc: /* Return t if memory profiler is running. */)
c2d7786e
TM
326 (void)
327{
328 return memory_profiler_running ? Qt : Qnil;
329}
330
331DEFUN ("memory-profiler-log",
332 Fmemory_profiler_log, Smemory_profiler_log,
333 0, 0, 0,
0efc778b
TM
334 doc: /* Return memory profiler log. The data is a list of
335(memory nil TIMESTAMP SLOTS), where TIMESTAMP is a timestamp when the
336log is collected and SLOTS is a list of slots. */)
c2d7786e
TM
337 (void)
338{
3d80c99f
SM
339 Lisp_Object result = memory_log;
340 /* Here we're making the log visible to Elisp , so it's not safe any
341 more for our use afterwards since we can't rely on its special
342 pre-allocated keys anymore. So we have to allocate a new one. */
343 memory_log = (memory_profiler_running
344 ? make_log (profiler_slot_heap_size, profiler_max_stack_depth)
345 : Qnil);
c2d7786e
TM
346 return result;
347}
348
349\f
0efc778b 350/* Signals and probes. */
c2d7786e 351
0efc778b 352/* Signal handler for sample profiler. */
c2d7786e
TM
353
354static void
355sigprof_handler (int signal, siginfo_t *info, void *ctx)
356{
3d80c99f
SM
357 eassert (HASH_TABLE_P (cpu_log));
358 if (backtrace_list && EQ (*backtrace_list->function, Qautomatic_gc))
359 /* Special case the time-count inside GC because the hash-table
360 code is not prepared to be used while the GC is running.
361 More specifically it uses ASIZE at many places where it does
362 not expect the ARRAY_MARK_FLAG to be set. We could try and
363 harden the hash-table code, but it doesn't seem worth the
364 effort. */
365 cpu_gc_count += current_sample_interval;
366 else
367 record_backtrace (XHASH_TABLE (cpu_log), current_sample_interval);
c2d7786e
TM
368}
369
0efc778b 370/* Record that the current backtrace allocated SIZE bytes. */
c2d7786e
TM
371void
372malloc_probe (size_t size)
373{
ad942b63
SM
374 eassert (HASH_TABLE_P (memory_log));
375 record_backtrace (XHASH_TABLE (memory_log), size);
c2d7786e
TM
376}
377
378void
379syms_of_profiler (void)
380{
381 DEFVAR_INT ("profiler-max-stack-depth", profiler_max_stack_depth,
382 doc: /* FIXME */);
383 profiler_max_stack_depth = 16;
384 DEFVAR_INT ("profiler-slot-heap-size", profiler_slot_heap_size,
385 doc: /* FIXME */);
386 profiler_slot_heap_size = 10000;
387
3d80c99f
SM
388 /* FIXME: Rename things to start with "profiler-", to use "cpu" instead of
389 "sample", and to make them sound like they're internal or something. */
ad942b63
SM
390#ifdef PROFILER_CPU_SUPPORT
391 cpu_log = Qnil;
392 staticpro (&cpu_log);
c2d7786e
TM
393 defsubr (&Ssample_profiler_start);
394 defsubr (&Ssample_profiler_stop);
c2d7786e
TM
395 defsubr (&Ssample_profiler_running_p);
396 defsubr (&Ssample_profiler_log);
ad942b63
SM
397#endif
398 memory_log = Qnil;
399 staticpro (&memory_log);
c2d7786e
TM
400 defsubr (&Smemory_profiler_start);
401 defsubr (&Smemory_profiler_stop);
c2d7786e
TM
402 defsubr (&Smemory_profiler_running_p);
403 defsubr (&Smemory_profiler_log);
404}