Mercurial > hg > xemacs-beta
comparison src/profile.c @ 1123:37bdd24225ef
[xemacs-hg @ 2002-11-27 07:15:02 by ben]
bug fixes, profiling debugging improvements
configure.in: Check for GCC version and only use -Wpacked in v3.
.cvsignore: Add .idb, .ilk for MS Windows VC++.
cl-macs.el: Document better.
cmdloop.el: Removed.
Remove nonworking breakpoint-on-error now that debug-on-error
works as documented.
help.el: Extract out with-displaying-help-buffer into a more general
mechanism.
lib-complete.el: Support thunks in find-library-source-path.
startup.el: Don't catch errors when noninteractive, because that makes
stack traces from stack-trace-on-error useless.
.cvsignore: Windows shit.
alloc.c: Better redisplay-related assert.
elhash.c: Comment change.
eval.c: Don't generate large warning strings (e.g. backtraces) when they will
be discarded.
Implement debug-on-error as documented -- it will enter the
debugger and crash when an uncaught signal happens noninteractively
and we are --debug.
Better redisplay-related asserts.
frame-msw.c, frame.c, lisp.h, redisplay.c, scrollbar-gtk.c, scrollbar-x.c, signal.c, sysdep.c: Fix up documentation related to QUIT (which CANNOT garbage-collect
under any circumstances), and to redisplay critical sections.
lread.c: Add load-ignore-out-of-date-elc-files,
load-always-display-messages, load-show-full-path-in-messages for
more robust package compilation and debugging.
profile.c: Overhaul profile code. Change format to include call count and be
extensible for further info. Remove call-count-profile-table.
Add set-profiling-info. See related profile.el changes (which
SHOULD ABSOLUTELY be in the core! Get rid of xemacs-devel and
xemacs-base packages *yesterday*!).
author | ben |
---|---|
date | Wed, 27 Nov 2002 07:15:36 +0000 |
parents | 2b6fa2618f76 |
children | f3437b56874d |
comparison
equal
deleted
inserted
replaced
1122:7abc2b15a990 | 1123:37bdd24225ef |
---|---|
1 /* Why the hell is XEmacs so fucking slow? | 1 /* Why the hell is XEmacs so fucking slow? |
2 Copyright (C) 1996 Ben Wing. | 2 Copyright (C) 1996, 2002 Ben Wing. |
3 Copyright (C) 1998 Free Software Foundation, Inc. | 3 Copyright (C) 1998 Free Software Foundation, Inc. |
4 | 4 |
5 This file is part of XEmacs. | 5 This file is part of XEmacs. |
6 | 6 |
7 XEmacs is free software; you can redistribute it and/or modify it | 7 XEmacs is free software; you can redistribute it and/or modify it |
48 | 48 |
49 It would be nice to use the Lisp allocation mechanism etc. to keep | 49 It would be nice to use the Lisp allocation mechanism etc. to keep |
50 track of the profiling information, but we can't because that's not | 50 track of the profiling information, but we can't because that's not |
51 safe, and trying to make it safe would be much more work than it's | 51 safe, and trying to make it safe would be much more work than it's |
52 worth. | 52 worth. |
53 | |
54 | 53 |
55 Jan 1998: In addition to this, I have added code to remember call | 54 Jan 1998: In addition to this, I have added code to remember call |
56 counts of Lisp funcalls. The profile_increase_call_count() | 55 counts of Lisp funcalls. The profile_increase_call_count() |
57 function is called from Ffuncall(), and serves to add data to | 56 function is called from Ffuncall(), and serves to add data to |
58 Vcall_count_profile_table. This mechanism is much simpler and | 57 Vcall_count_profile_table. This mechanism is much simpler and |
75 static Lisp_Object QSin_redisplay; | 74 static Lisp_Object QSin_redisplay; |
76 static Lisp_Object QSin_garbage_collection; | 75 static Lisp_Object QSin_garbage_collection; |
77 static Lisp_Object QSprocessing_events_at_top_level; | 76 static Lisp_Object QSprocessing_events_at_top_level; |
78 static Lisp_Object QSunknown; | 77 static Lisp_Object QSunknown; |
79 | 78 |
79 static Lisp_Object Qtiming, Qcall_count; | |
80 | |
80 /* We use inside_profiling to prevent the handler from writing to | 81 /* We use inside_profiling to prevent the handler from writing to |
81 the table while another routine is operating on it. We also set | 82 the table while another routine is operating on it. We also set |
82 inside_profiling in case the timeout between signal calls is short | 83 inside_profiling in case the timeout between signal calls is short |
83 enough to catch us while we're already in there. */ | 84 enough to catch us while we're already in there. */ |
84 static volatile int inside_profiling; | 85 static volatile int inside_profiling; |
85 | 86 |
87 static void | |
88 create_call_count_profile_table (void) | |
89 { | |
90 if (NILP (Vcall_count_profile_table)) | |
91 Vcall_count_profile_table = | |
92 make_lisp_hash_table (100, HASH_TABLE_NON_WEAK, HASH_TABLE_EQ); | |
93 } | |
94 | |
95 static void | |
96 create_timing_profile_table (void) | |
97 { | |
98 /* #### The hash code can safely be called from a signal handler | |
99 except when it has to grow the hash table. In this case, it calls | |
100 realloc(), which is not (in general) re-entrant. We'll just be | |
101 sleazy and make the table large enough that it (hopefully) won't | |
102 need to be realloc()ed. */ | |
103 if (!big_profile_table) | |
104 big_profile_table = make_hash_table (10000); | |
105 } | |
106 | |
86 /* Increase the value of OBJ in Vcall_count_profile_table hash table. | 107 /* Increase the value of OBJ in Vcall_count_profile_table hash table. |
87 If the hash table is nil, create it first. */ | 108 If the hash table is nil, create it first. */ |
88 void | 109 void |
89 profile_increase_call_count (Lisp_Object obj) | 110 profile_increase_call_count (Lisp_Object obj) |
90 { | 111 { |
91 Lisp_Object count; | 112 Lisp_Object count; |
92 | 113 |
93 if (NILP (Vcall_count_profile_table)) | 114 create_call_count_profile_table (); |
94 Vcall_count_profile_table = | |
95 make_lisp_hash_table (100, HASH_TABLE_NON_WEAK, HASH_TABLE_EQ); | |
96 | 115 |
97 count = Fgethash (obj, Vcall_count_profile_table, Qzero); | 116 count = Fgethash (obj, Vcall_count_profile_table, Qzero); |
98 if (!INTP (count)) | 117 if (!INTP (count)) |
99 count = Qzero; | 118 count = Qzero; |
100 Fputhash (obj, make_int (1 + XINT (count)), Vcall_count_profile_table); | 119 Fputhash (obj, make_int (1 + XINT (count)), Vcall_count_profile_table); |
157 DEFUN ("start-profiling", Fstart_profiling, 0, 1, 0, /* | 176 DEFUN ("start-profiling", Fstart_profiling, 0, 1, 0, /* |
158 Start profiling, with profile queries every MICROSECS. | 177 Start profiling, with profile queries every MICROSECS. |
159 If MICROSECS is nil or omitted, the value of `default-profiling-interval' | 178 If MICROSECS is nil or omitted, the value of `default-profiling-interval' |
160 is used. | 179 is used. |
161 | 180 |
181 Information on function timings and call counts is currently recorded. | |
162 You can retrieve the recorded profiling info using `get-profiling-info'. | 182 You can retrieve the recorded profiling info using `get-profiling-info'. |
163 | 183 |
164 Starting and stopping profiling does not clear the currently recorded | 184 Starting and stopping profiling does not clear the currently recorded |
165 info. Thus you can start and stop as many times as you want and everything | 185 info. Thus you can start and stop as many times as you want and everything |
166 will be properly accumulated. | 186 will be properly accumulated. |
174 /* #### The hash code can safely be called from a signal handler | 194 /* #### The hash code can safely be called from a signal handler |
175 except when it has to grow the hash table. In this case, it calls | 195 except when it has to grow the hash table. In this case, it calls |
176 realloc(), which is not (in general) re-entrant. We'll just be | 196 realloc(), which is not (in general) re-entrant. We'll just be |
177 sleazy and make the table large enough that it (hopefully) won't | 197 sleazy and make the table large enough that it (hopefully) won't |
178 need to be realloc()ed. */ | 198 need to be realloc()ed. */ |
179 if (!big_profile_table) | 199 create_timing_profile_table (); |
180 big_profile_table = make_hash_table (10000); | |
181 | 200 |
182 if (NILP (microsecs)) | 201 if (NILP (microsecs)) |
183 msecs = default_profiling_interval; | 202 msecs = default_profiling_interval; |
184 else | 203 else |
185 { | 204 { |
215 profiling_active = 0; | 234 profiling_active = 0; |
216 set_timeout_signal (SIGPROF, fatal_error_signal); | 235 set_timeout_signal (SIGPROF, fatal_error_signal); |
217 return Qnil; | 236 return Qnil; |
218 } | 237 } |
219 | 238 |
220 struct get_profiling_info_closure | |
221 { | |
222 Lisp_Object accum; | |
223 }; | |
224 | |
225 static int | |
226 get_profiling_info_maphash (const void *void_key, | |
227 void *void_val, | |
228 void *void_closure) | |
229 { | |
230 /* This function does not GC */ | |
231 Lisp_Object key; | |
232 struct get_profiling_info_closure *closure | |
233 = (struct get_profiling_info_closure *) void_closure; | |
234 EMACS_INT val; | |
235 | |
236 key = VOID_TO_LISP (void_key); | |
237 val = (EMACS_INT) void_val; | |
238 | |
239 closure->accum = Fcons (Fcons (key, make_int (val)), closure->accum); | |
240 return 0; | |
241 } | |
242 | |
243 DEFUN ("get-profiling-info", Fget_profiling_info, 0, 0, 0, /* | |
244 Return the profiling info as an alist. | |
245 */ | |
246 ()) | |
247 { | |
248 /* This function does not GC */ | |
249 struct get_profiling_info_closure closure; | |
250 | |
251 closure.accum = Qnil; | |
252 if (big_profile_table) | |
253 { | |
254 int count = internal_bind_int ((int *) &inside_profiling, 1); | |
255 maphash (get_profiling_info_maphash, big_profile_table, &closure); | |
256 unbind_to (count); | |
257 } | |
258 return closure.accum; | |
259 } | |
260 | |
261 static int | |
262 mark_profiling_info_maphash (const void *void_key, | |
263 void *void_val, | |
264 void *void_closure) | |
265 { | |
266 Lisp_Object key; | |
267 | |
268 key = VOID_TO_LISP (void_key); | |
269 mark_object (key); | |
270 return 0; | |
271 } | |
272 | |
273 void | |
274 mark_profiling_info (void) | |
275 { | |
276 /* This function does not GC */ | |
277 if (big_profile_table) | |
278 { | |
279 inside_profiling = 1; | |
280 maphash (mark_profiling_info_maphash, big_profile_table, 0); | |
281 inside_profiling = 0; | |
282 } | |
283 } | |
284 | |
285 DEFUN ("clear-profiling-info", Fclear_profiling_info, 0, 0, "", /* | 239 DEFUN ("clear-profiling-info", Fclear_profiling_info, 0, 0, "", /* |
286 Clear out the recorded profiling info. | 240 Clear out the recorded profiling info. |
241 This clears both the internal timing information and the call counts in | |
242 `call-count-profile-table'. | |
287 */ | 243 */ |
288 ()) | 244 ()) |
289 { | 245 { |
290 /* This function does not GC */ | 246 /* This function does not GC */ |
291 if (big_profile_table) | 247 if (big_profile_table) |
297 if (!NILP (Vcall_count_profile_table)) | 253 if (!NILP (Vcall_count_profile_table)) |
298 Fclrhash (Vcall_count_profile_table); | 254 Fclrhash (Vcall_count_profile_table); |
299 return Qnil; | 255 return Qnil; |
300 } | 256 } |
301 | 257 |
258 struct get_profiling_info_closure | |
259 { | |
260 Lisp_Object timing; | |
261 }; | |
262 | |
263 static int | |
264 get_profiling_info_timing_maphash (const void *void_key, | |
265 void *void_val, | |
266 void *void_closure) | |
267 { | |
268 /* This function does not GC */ | |
269 Lisp_Object key; | |
270 struct get_profiling_info_closure *closure | |
271 = (struct get_profiling_info_closure *) void_closure; | |
272 EMACS_INT val; | |
273 | |
274 key = VOID_TO_LISP (void_key); | |
275 val = (EMACS_INT) void_val; | |
276 | |
277 Fputhash (key, make_int (val), closure->timing); | |
278 return 0; | |
279 } | |
280 | |
281 DEFUN ("get-profiling-info", Fget_profiling_info, 0, 0, 0, /* | |
282 Return the currently recorded profiling info. | |
283 The format is a plist of symbols describing type of info recorded and | |
284 an associated type-specific entry. Currently, the following info types | |
285 are recorded | |
286 | |
287 `timing' | |
288 A hash table of funcallable objects or strings describing internal processing | |
289 operations \(redisplay, garbage collection, etc.), along with associated | |
290 tick counts (the frequency of ticks is controlled by | |
291 `default-profiling-interval' or the argument to `start-profiling'). | |
292 | |
293 `call-count' | |
294 A hash table of funcallable objects and associated call counts. | |
295 */ | |
296 ()) | |
297 { | |
298 /* This function does not GC */ | |
299 struct get_profiling_info_closure closure; | |
300 | |
301 closure.timing = | |
302 make_lisp_hash_table (100, HASH_TABLE_NON_WEAK, HASH_TABLE_EQUAL); | |
303 | |
304 if (big_profile_table) | |
305 { | |
306 int count = internal_bind_int ((int *) &inside_profiling, 1); | |
307 maphash (get_profiling_info_timing_maphash, big_profile_table, &closure); | |
308 unbind_to (count); | |
309 } | |
310 | |
311 return list4 (Qtiming, closure.timing, Qcall_count, | |
312 !NILP (Vcall_count_profile_table) ? | |
313 Fcopy_hash_table (Vcall_count_profile_table) : | |
314 make_lisp_hash_table (100, HASH_TABLE_NON_WEAK, | |
315 HASH_TABLE_EQ)); | |
316 } | |
317 | |
318 struct set_profiling_info_closure | |
319 { | |
320 Lisp_Object timing; | |
321 }; | |
322 | |
323 static int | |
324 set_profiling_info_timing_maphash (Lisp_Object key, | |
325 Lisp_Object val, | |
326 void *void_closure) | |
327 { | |
328 /* This function does not GC */ | |
329 if (!INTP (val)) | |
330 invalid_argument_2 | |
331 ("Function timing count is not an integer in given entry", | |
332 key, val); | |
333 | |
334 puthash (LISP_TO_VOID (key), (void *) XINT (val), big_profile_table); | |
335 | |
336 return 0; | |
337 } | |
338 | |
339 DEFUN ("set-profiling-info", Fset_profiling_info, 1, 1, 0, /* | |
340 Set the currently recorded profiling info. | |
341 INFO should be in the same format returned by `get-profiling-info', | |
342 as described there. | |
343 */ | |
344 (info)) | |
345 { | |
346 /* This function does not GC */ | |
347 Fclear_profiling_info (); | |
348 | |
349 { | |
350 EXTERNAL_PROPERTY_LIST_LOOP_3 (key, value, info) | |
351 { | |
352 if (EQ (key, Qtiming)) | |
353 { | |
354 CHECK_HASH_TABLE (value); | |
355 create_timing_profile_table (); | |
356 elisp_maphash_unsafe (set_profiling_info_timing_maphash, value, | |
357 NULL); | |
358 } | |
359 else if (EQ (key, Qcall_count)) | |
360 { | |
361 Vcall_count_profile_table = Fcopy_hash_table (value); | |
362 } | |
363 else | |
364 invalid_constant ("Unrecognized profiling-info keyword", key); | |
365 } | |
366 } | |
367 | |
368 return Qnil; | |
369 } | |
370 | |
371 static int | |
372 mark_profiling_info_maphash (const void *void_key, | |
373 void *void_val, | |
374 void *void_closure) | |
375 { | |
376 Lisp_Object key; | |
377 | |
378 key = VOID_TO_LISP (void_key); | |
379 mark_object (key); | |
380 return 0; | |
381 } | |
382 | |
383 void | |
384 mark_profiling_info (void) | |
385 { | |
386 /* This function does not GC */ | |
387 if (big_profile_table) | |
388 { | |
389 inside_profiling = 1; | |
390 maphash (mark_profiling_info_maphash, big_profile_table, 0); | |
391 inside_profiling = 0; | |
392 } | |
393 } | |
394 | |
302 DEFUN ("profiling-active-p", Fprofiling_active_p, 0, 0, 0, /* | 395 DEFUN ("profiling-active-p", Fprofiling_active_p, 0, 0, 0, /* |
303 Return non-nil if profiling information is currently being recorded. | 396 Return non-nil if profiling information is currently being recorded. |
304 */ | 397 */ |
305 ()) | 398 ()) |
306 { | 399 { |
311 syms_of_profile (void) | 404 syms_of_profile (void) |
312 { | 405 { |
313 DEFSUBR (Fstart_profiling); | 406 DEFSUBR (Fstart_profiling); |
314 DEFSUBR (Fstop_profiling); | 407 DEFSUBR (Fstop_profiling); |
315 DEFSUBR (Fget_profiling_info); | 408 DEFSUBR (Fget_profiling_info); |
409 DEFSUBR (Fset_profiling_info); | |
316 DEFSUBR (Fclear_profiling_info); | 410 DEFSUBR (Fclear_profiling_info); |
317 DEFSUBR (Fprofiling_active_p); | 411 DEFSUBR (Fprofiling_active_p); |
318 } | 412 } |
319 | 413 |
320 void | 414 void |
322 { | 416 { |
323 DEFVAR_INT ("default-profiling-interval", &default_profiling_interval /* | 417 DEFVAR_INT ("default-profiling-interval", &default_profiling_interval /* |
324 Default CPU time in microseconds between profiling sampling. | 418 Default CPU time in microseconds between profiling sampling. |
325 Used when the argument to `start-profiling' is nil or omitted. | 419 Used when the argument to `start-profiling' is nil or omitted. |
326 Note that the time in question is CPU time (when the program is executing | 420 Note that the time in question is CPU time (when the program is executing |
327 or the kernel is executing on behalf of the program) and not real time. | 421 or the kernel is executing on behalf of the program) and not real time, and |
422 there is usually a machine-dependent limit on how small this value can be. | |
328 */ ); | 423 */ ); |
329 default_profiling_interval = 1000; | 424 default_profiling_interval = 1000; |
330 | 425 |
331 DEFVAR_LISP ("call-count-profile-table", &Vcall_count_profile_table /* | 426 staticpro (&Vcall_count_profile_table); |
332 The table where call-count information is stored by the profiling primitives. | |
333 This is a hash table whose keys are funcallable objects, and whose | |
334 values are their call counts (integers). | |
335 */ ); | |
336 Vcall_count_profile_table = Qnil; | 427 Vcall_count_profile_table = Qnil; |
337 | 428 |
338 inside_profiling = 0; | 429 inside_profiling = 0; |
339 | 430 |
340 QSin_redisplay = build_msg_string ("(in redisplay)"); | 431 QSin_redisplay = build_msg_string ("(in redisplay)"); |
344 QSunknown = build_msg_string ("(unknown)"); | 435 QSunknown = build_msg_string ("(unknown)"); |
345 staticpro (&QSunknown); | 436 staticpro (&QSunknown); |
346 QSprocessing_events_at_top_level = | 437 QSprocessing_events_at_top_level = |
347 build_msg_string ("(processing events at top level)"); | 438 build_msg_string ("(processing events at top level)"); |
348 staticpro (&QSprocessing_events_at_top_level); | 439 staticpro (&QSprocessing_events_at_top_level); |
349 } | 440 |
441 DEFSYMBOL (Qtiming); | |
442 DEFSYMBOL (Qcall_count); | |
443 } |