comparison src/profile.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 9d177e8d4150
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
1 /* Why the hell is XEmacs so fucking slow?
2 Copyright (C) 1996 Ben Wing.
3 Copyright (C) 1998 Free Software Foundation, Inc.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22 #include <config.h>
23 #include "lisp.h"
24
25 #include "backtrace.h"
26 #include "bytecode.h"
27 #include "elhash.h"
28 #include "hash.h"
29
30 #include "syssignal.h"
31 #include "systime.h"
32
33 /* We implement our own profiling scheme so that we can determine
34 things like which Lisp functions are occupying the most time. Any
35 standard OS-provided profiling works on C functions, which is
36 somewhat useless.
37
38 The basic idea is simple. We set a profiling timer using setitimer
39 (ITIMER_PROF), which generates a SIGPROF every so often. (This
40 runs not in real time but rather when the process is executing or
41 the system is running on behalf of the process.) When the signal
42 goes off, we see what we're in, and add 1 to the count associated
43 with that function.
44
45 It would be nice to use the Lisp allocation mechanism etc. to keep
46 track of the profiling information, but we can't because that's not
47 safe, and trying to make it safe would be much more work than it's
48 worth.
49
50
51 Jan 1998: In addition to this, I have added code to remember call
52 counts of Lisp funcalls. The profile_increase_call_count()
53 function is called from Ffuncall(), and serves to add data to
54 Vcall_count_profile_table. This mechanism is much simpler and
55 independent of the SIGPROF-driven one. It uses the Lisp allocation
56 mechanism normally, since it is not called from a handler. It may
57 even be useful to provide a way to turn on only one profiling
58 mechanism, but I haven't done so yet. --hniksic */
59
60 static struct hash_table *big_profile_table;
61 Lisp_Object Vcall_count_profile_table;
62
63 int default_profiling_interval;
64
65 int profiling_active;
66
67 /* The normal flag in_display is used as a critical-section flag
68 and is not set the whole time we're in redisplay. */
69 int profiling_redisplay_flag;
70
71 static Lisp_Object QSin_redisplay;
72 static Lisp_Object QSin_garbage_collection;
73 static Lisp_Object QSprocessing_events_at_top_level;
74 static Lisp_Object QSunknown;
75
76 /* We use inside_profiling to prevent the handler from writing to
77 the table while another routine is operating on it. We also set
78 inside_profiling in case the timeout between signal calls is short
79 enough to catch us while we're already in there. */
80 static volatile int inside_profiling;
81
82 /* Increase the value of OBJ in Vcall_count_profile_table hash table.
83 If the hash table is nil, create it first. */
84 void
85 profile_increase_call_count (Lisp_Object obj)
86 {
87 Lisp_Object count;
88
89 if (NILP (Vcall_count_profile_table))
90 Vcall_count_profile_table =
91 make_lisp_hash_table (100, HASH_TABLE_NON_WEAK, HASH_TABLE_EQ);
92
93 count = Fgethash (obj, Vcall_count_profile_table, Qzero);
94 if (!INTP (count))
95 count = Qzero;
96 Fputhash (obj, make_int (1 + XINT (count)), Vcall_count_profile_table);
97 }
98
99 static SIGTYPE
100 sigprof_handler (int signo)
101 {
102 /* Don't do anything if we are shutting down, or are doing a maphash
103 or clrhash on the table. */
104 if (!inside_profiling && !preparing_for_armageddon)
105 {
106 Lisp_Object fun;
107
108 /* If something below causes an error to be signaled, we'll
109 not correctly reset this flag. But we'll be in worse shape
110 than that anyways, since we'll longjmp back to the last
111 condition case. */
112 inside_profiling = 1;
113
114 if (profiling_redisplay_flag)
115 fun = QSin_redisplay;
116 else if (gc_in_progress)
117 fun = QSin_garbage_collection;
118 else if (backtrace_list)
119 {
120 fun = *backtrace_list->function;
121
122 if (!SYMBOLP (fun) &&
123 !COMPILED_FUNCTIONP (fun) &&
124 !SUBRP (fun))
125 fun = QSunknown;
126 }
127 else
128 fun = QSprocessing_events_at_top_level;
129
130 {
131 /* #### see comment about memory allocation in start-profiling.
132 Allocating memory in a signal handler is BAD BAD BAD.
133 If you are using the non-mmap rel-alloc code, you might
134 lose because of this. Even worse, if the memory allocation
135 fails, the `error' generated whacks everything hard. */
136 long count;
137 CONST void *vval;
138
139 if (gethash (LISP_TO_VOID (fun), big_profile_table, &vval))
140 count = (long) vval;
141 else
142 count = 0;
143 count++;
144 vval = (CONST void *) count;
145 puthash (LISP_TO_VOID (fun), (void *) vval, big_profile_table);
146 }
147
148 inside_profiling = 0;
149 }
150 }
151
152 DEFUN ("start-profiling", Fstart_profiling, 0, 1, 0, /*
153 Start profiling, with profile queries every MICROSECS.
154 If MICROSECS is nil or omitted, the value of `default-profiling-interval'
155 is used.
156
157 You can retrieve the recorded profiling info using `get-profiling-info'.
158
159 Starting and stopping profiling does not clear the currently recorded
160 info. Thus you can start and stop as many times as you want and everything
161 will be properly accumulated.
162 */
163 (microsecs))
164 {
165 /* This function can GC */
166 int msecs;
167 struct itimerval foo;
168
169 /* #### The hash code can safely be called from a signal handler
170 except when it has to grow the hash table. In this case, it calls
171 realloc(), which is not (in general) re-entrant. We'll just be
172 sleazy and make the table large enough that it (hopefully) won't
173 need to be realloc()ed. */
174 if (!big_profile_table)
175 big_profile_table = make_hash_table (10000);
176
177 if (NILP (microsecs))
178 msecs = default_profiling_interval;
179 else
180 {
181 CHECK_NATNUM (microsecs);
182 msecs = XINT (microsecs);
183 }
184 if (msecs <= 0)
185 msecs = 1000;
186
187 signal (SIGPROF, sigprof_handler);
188 foo.it_value.tv_sec = 0;
189 foo.it_value.tv_usec = msecs;
190 EMACS_NORMALIZE_TIME (foo.it_value);
191 foo.it_interval = foo.it_value;
192 profiling_active = 1;
193 inside_profiling = 0;
194 setitimer (ITIMER_PROF, &foo, 0);
195 return Qnil;
196 }
197
198 DEFUN ("stop-profiling", Fstop_profiling, 0, 0, 0, /*
199 Stop profiling.
200 */
201 ())
202 {
203 /* This function does not GC */
204 struct itimerval foo;
205
206 foo.it_value.tv_sec = 0;
207 foo.it_value.tv_usec = 0;
208 foo.it_interval = foo.it_value;
209 setitimer (ITIMER_PROF, &foo, 0);
210 profiling_active = 0;
211 signal (SIGPROF, fatal_error_signal);
212 return Qnil;
213 }
214
215 static Lisp_Object
216 profile_lock_unwind (Lisp_Object ignore)
217 {
218 inside_profiling = 0;
219 return Qnil;
220 }
221
222 struct get_profiling_info_closure
223 {
224 Lisp_Object accum;
225 };
226
227 static int
228 get_profiling_info_maphash (CONST void *void_key,
229 void *void_val,
230 void *void_closure)
231 {
232 /* This function does not GC */
233 Lisp_Object key;
234 struct get_profiling_info_closure *closure
235 = (struct get_profiling_info_closure *) void_closure;
236 EMACS_INT val;
237
238 CVOID_TO_LISP (key, void_key);
239 val = (EMACS_INT) void_val;
240
241 closure->accum = Fcons (Fcons (key, make_int (val)), closure->accum);
242 return 0;
243 }
244
245 DEFUN ("get-profiling-info", Fget_profiling_info, 0, 0, 0, /*
246 Return the profiling info as an alist.
247 */
248 ())
249 {
250 /* This function does not GC */
251 struct get_profiling_info_closure closure;
252
253 closure.accum = Qnil;
254 if (big_profile_table)
255 {
256 int count = specpdl_depth ();
257 record_unwind_protect (profile_lock_unwind, Qnil);
258 inside_profiling = 1;
259 maphash (get_profiling_info_maphash, big_profile_table, &closure);
260 unbind_to (count, Qnil);
261 }
262 return closure.accum;
263 }
264
265 static int
266 mark_profiling_info_maphash (CONST void *void_key,
267 void *void_val,
268 void *void_closure)
269 {
270 Lisp_Object key;
271
272 CVOID_TO_LISP (key, void_key);
273 mark_object (key);
274 return 0;
275 }
276
277 void
278 mark_profiling_info (void)
279 {
280 /* This function does not GC */
281 if (big_profile_table)
282 {
283 inside_profiling = 1;
284 maphash (mark_profiling_info_maphash, big_profile_table, 0);
285 inside_profiling = 0;
286 }
287 }
288
289 DEFUN ("clear-profiling-info", Fclear_profiling_info, 0, 0, "", /*
290 Clear out the recorded profiling info.
291 */
292 ())
293 {
294 /* This function does not GC */
295 if (big_profile_table)
296 {
297 inside_profiling = 1;
298 clrhash (big_profile_table);
299 inside_profiling = 0;
300 }
301 if (!NILP (Vcall_count_profile_table))
302 Fclrhash (Vcall_count_profile_table);
303 return Qnil;
304 }
305
306 DEFUN ("profiling-active-p", Fprofiling_active_p, 0, 0, 0, /*
307 Return non-nil if profiling information is currently being recorded.
308 */
309 ())
310 {
311 return profiling_active ? Qt : Qnil;
312 }
313
314 void
315 syms_of_profile (void)
316 {
317 DEFSUBR (Fstart_profiling);
318 DEFSUBR (Fstop_profiling);
319 DEFSUBR (Fget_profiling_info);
320 DEFSUBR (Fclear_profiling_info);
321 DEFSUBR (Fprofiling_active_p);
322 }
323
324 void
325 vars_of_profile (void)
326 {
327 DEFVAR_INT ("default-profiling-interval", &default_profiling_interval /*
328 Default CPU time in microseconds between profiling sampling.
329 Used when the argument to `start-profiling' is nil or omitted.
330 Note that the time in question is CPU time (when the program is executing
331 or the kernel is executing on behalf of the program) and not real time.
332 */ );
333 default_profiling_interval = 1000;
334
335 DEFVAR_LISP ("call-count-profile-table", &Vcall_count_profile_table /*
336 The table where call-count information is stored by the profiling primitives.
337 This is a hash table whose keys are funcallable objects, and whose
338 values are their call counts (integers).
339 */ );
340 Vcall_count_profile_table = Qnil;
341
342 inside_profiling = 0;
343
344 QSin_redisplay = build_string ("(in redisplay)");
345 staticpro (&QSin_redisplay);
346 QSin_garbage_collection = build_string ("(in garbage collection)");
347 staticpro (&QSin_garbage_collection);
348 QSunknown = build_string ("(unknown)");
349 staticpro (&QSunknown);
350 QSprocessing_events_at_top_level =
351 build_string ("(processing events at top level)");
352 staticpro (&QSprocessing_events_at_top_level);
353 }