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 }