a profiler

cvs
Slava Pestov 2004-08-23 05:13:09 +00:00
parent b23622f947
commit c66ded6bf8
21 changed files with 208 additions and 70 deletions

View File

@ -5,6 +5,8 @@
- multiple tasks should be able to write to the same port - multiple tasks should be able to write to the same port
- jvm factor -- still supporting httpd? - jvm factor -- still supporting httpd?
- make inferior.factor nicer to use - make inferior.factor nicer to use
- telnetd printing signal 13, and other problems
- check error callstack, not enough >pop>?
+ docs: + docs:
@ -35,6 +37,9 @@
+ native: + native:
- is the profiler using correct stack depth?
- bignums
- >lower, >upper for strings
- read1 - read1
- telnetd and httpd should use multitasking - telnetd and httpd should use multitasking
- sbuf-hashcode - sbuf-hashcode

View File

@ -34,7 +34,10 @@ USE: namespaces
USE: stack USE: stack
USE: vectors USE: vectors
: reify datastack >pop> callstack >pop> namestack catchstack ; : reify ( quot -- )
>r datastack >pop> callstack >pop> namestack catchstack
r> call ;
: (callcc) cons cons cons cons swap call ; : (callcc) cons cons cons cons swap call ;
: continue0 ( ds rs ns cs -- ) : continue0 ( ds rs ns cs -- )
@ -49,7 +52,7 @@ USE: vectors
#! #!
#! When called, the quotation restores execution state to #! When called, the quotation restores execution state to
#! the point after the callcc0 call. #! the point after the callcc0 call.
reify [ continue0 ] (callcc) ; [ [ continue0 ] (callcc) ] reify ;
: continue1 ( obj ds rs ns cs -- obj ) : continue1 ( obj ds rs ns cs -- obj )
set-catchstack set-namestack set-catchstack set-namestack
@ -64,7 +67,7 @@ USE: vectors
#! When called, the quotation restores execution state to #! When called, the quotation restores execution state to
#! the point after the callcc1 call, and places X at the top #! the point after the callcc1 call, and places X at the top
#! of the original datastack. #! of the original datastack.
reify [ continue1 ] (callcc) ; [ [ continue1 ] (callcc) ] reify ;
: suspend ( -- ) : suspend ( -- )
"top-level-continuation" get dup [ "top-level-continuation" get dup [

View File

@ -81,6 +81,11 @@ DEFER: next-io-task
IN: parser IN: parser
DEFER: str>float DEFER: str>float
IN: profiler
DEFER: profiling
DEFER: call-count
DEFER: set-call-count
IN: random IN: random
DEFER: init-random DEFER: init-random
DEFER: (random-int) DEFER: (random-int)
@ -236,6 +241,9 @@ IN: cross-compiler
(random-int) (random-int)
type-of type-of
size-of size-of
profiling
call-count
set-call-count
] [ ] [
swap succ tuck primitive, swap succ tuck primitive,
] each drop ; ] each drop ;

View File

@ -103,6 +103,7 @@ USE: stdio
"/library/ansi.factor" "/library/ansi.factor"
"/library/telnetd.factor" "/library/telnetd.factor"
"/library/inferior.factor" "/library/inferior.factor"
"/library/platform/native/profiler.factor"
"/library/image.factor" "/library/image.factor"
"/library/cross-compiler.factor" "/library/cross-compiler.factor"

View File

@ -79,6 +79,9 @@ USE: vectors
: no-io-tasks-error ( obj -- ) : no-io-tasks-error ( obj -- )
"No I/O tasks" print ; "No I/O tasks" print ;
: profiling-disabled-error ( obj -- )
drop "Recompile with the EXTRA_CALL_INFO flag." print ;
: kernel-error. ( obj n -- str ) : kernel-error. ( obj n -- str )
{ {
expired-port-error expired-port-error

View File

@ -97,6 +97,8 @@ USE: vectors
[ 12 | "port" ] [ 12 | "port" ]
[ 13 | "bignum" ] [ 13 | "bignum" ]
[ 14 | "float" ] [ 14 | "float" ]
! These values are only used by the kernel for error
! reporting.
[ 100 | "fixnum/bignum" ] [ 100 | "fixnum/bignum" ]
[ 101 | "fixnum/bignum/ratio" ] [ 101 | "fixnum/bignum/ratio" ]
[ 102 | "fixnum/bignum/ratio/float" ] [ 102 | "fixnum/bignum/ratio/float" ]

View File

@ -0,0 +1,61 @@
! :folding=indent:collapseFolds=1:
! $Id$
!
! Copyright (C) 2004 Slava Pestov.
!
! Redistribution and use in source and binary forms, with or without
! modification, are permitted provided that the following conditions are met:
!
! 1. Redistributions of source code must retain the above copyright notice,
! this list of conditions and the following disclaimer.
!
! 2. Redistributions in binary form must reproduce the above copyright notice,
! this list of conditions and the following disclaimer in the documentation
! and/or other materials provided with the distribution.
!
! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
IN: profiler
USE: arithmetic
USE: kernel
USE: lists
USE: prettyprint
USE: stack
USE: words
USE: vectors
: reset-call-counts ( -- )
vocabs [ words [ 0 swap set-call-count ] each ] each ;
: sort-call-counts ( alist -- alist )
[ swap cdr swap cdr > ] sort ;
: call-count, ( word -- )
#! Add to constructing list if call count is non-zero.
dup call-count dup 0 = [
2drop
] [
cons ,
] ifte ;
: call-counts ( -- alist )
#! Push an alist of all word/call count pairs.
[, [ call-count, ] each-word ,] sort-call-counts ;
: profile ( quot -- )
#! Execute a quotation with the profiler enabled.
reset-call-counts
callstack vector-length profiling
call
f profiling
call-counts [ . ] each ;

View File

@ -13,4 +13,4 @@ USE: threads
[ yield 2 "x" set ] in-thread [ yield 2 "x" set ] in-thread
[ 2 ] [ yield "x" get ] unit-test [ 2 ] [ yield "x" get ] unit-test
[ flush ] in-thread flush ! [ flush ] in-thread flush

View File

@ -44,3 +44,7 @@ USE: stack
: set-word-vocabulary ( word vocab -- ) : set-word-vocabulary ( word vocab -- )
"vocabulary" swap set-word-property ; "vocabulary" swap set-word-property ;
: each-word ( quot -- )
#! Apply a quotation to each word in the image.
vocabs [ words [ swap dup >r call r> ] each ] each drop ;

View File

@ -29,8 +29,7 @@ void throw_error(CELL error)
dpush(error); dpush(error);
/* Execute the 'throw' word */ /* Execute the 'throw' word */
cpush(callframe); call(userenv[BREAK_ENV]);
callframe = userenv[BREAK_ENV];
if(callframe == 0) if(callframe == 0)
{ {
/* Crash at startup */ /* Crash at startup */

View File

@ -8,6 +8,7 @@
#define ERROR_SIGNAL (7<<3) #define ERROR_SIGNAL (7<<3)
#define ERROR_IO_TASK_TWICE (8<<3) #define ERROR_IO_TASK_TWICE (8<<3)
#define ERROR_IO_TASK_NONE (9<<3) #define ERROR_IO_TASK_NONE (9<<3)
#define ERROR_PROFILING_DISABLED (10<<3)
void fatal_error(char* msg, CELL tagged); void fatal_error(char* msg, CELL tagged);
void critical_error(char* msg, CELL tagged); void critical_error(char* msg, CELL tagged);

View File

@ -36,6 +36,10 @@ typedef unsigned short CHAR;
#define STACK_SIZE 16384 #define STACK_SIZE 16384
/* This decreases performance slightly but gives more readable backtraces,
and allows profiling. */
#define EXTRA_CALL_INFO
#include "error.h" #include "error.h"
#include "memory.h" #include "memory.h"
#include "gc.h" #include "gc.h"

View File

@ -54,8 +54,7 @@ void check_memory(void)
} }
/* Execute the 'garbage-collection' word */ /* Execute the 'garbage-collection' word */
cpush(callframe); call(userenv[GC_ENV]);
callframe = userenv[GC_ENV];
} }
} }

View File

@ -2,7 +2,7 @@
XT primitives[] = { XT primitives[] = {
undefined, undefined,
call, docol,
primitive_execute, primitive_execute,
primitive_call, primitive_call,
primitive_ifte, primitive_ifte,
@ -137,7 +137,10 @@ XT primitives[] = {
primitive_init_random, primitive_init_random,
primitive_random_int, primitive_random_int,
primitive_type_of, primitive_type_of,
primitive_size_of primitive_size_of,
primitive_profiling,
primitive_word_call_count,
primitive_set_word_call_count
}; };
CELL primitive_to_xt(CELL primitive) CELL primitive_to_xt(CELL primitive)

View File

@ -1,4 +1,4 @@
extern XT primitives[]; extern XT primitives[];
#define PRIMITIVE_COUNT 137 #define PRIMITIVE_COUNT 140
CELL primitive_to_xt(CELL primitive); CELL primitive_to_xt(CELL primitive);

View File

@ -5,19 +5,36 @@ void signal_handler(int signal, siginfo_t* siginfo, void* uap)
general_error(ERROR_SIGNAL,tag_fixnum(signal)); general_error(ERROR_SIGNAL,tag_fixnum(signal));
} }
/* Called from a signal handler. XXX - is this safe? */
void profiling_step(int signal, siginfo_t* siginfo, void* uap)
{
CELL depth = (cs - cs_bot) / CELLS;
int i;
CELL obj;
for(i = profile_depth; i < depth; i++)
{
obj = get(cs_bot + i * CELLS);
if(TAG(obj) == WORD_TYPE)
untag_word(obj)->call_count++;
}
executing->call_count++;
}
void init_signals(void) void init_signals(void)
{ {
struct sigaction custom_sigaction; struct sigaction custom_sigaction;
struct sigaction ign_sigaction; struct sigaction profiling_sigaction;
custom_sigaction.sa_sigaction = signal_handler; custom_sigaction.sa_sigaction = signal_handler;
custom_sigaction.sa_flags = SA_SIGINFO; custom_sigaction.sa_flags = SA_SIGINFO;
ign_sigaction.sa_handler = SIG_IGN; profiling_sigaction.sa_sigaction = profiling_step;
ign_sigaction.sa_flags = 0; profiling_sigaction.sa_flags = SA_SIGINFO;
sigaction(SIGABRT,&custom_sigaction,NULL); sigaction(SIGABRT,&custom_sigaction,NULL);
sigaction(SIGFPE,&custom_sigaction,NULL); sigaction(SIGFPE,&custom_sigaction,NULL);
sigaction(SIGBUS,&custom_sigaction,NULL); sigaction(SIGBUS,&custom_sigaction,NULL);
sigaction(SIGSEGV,&custom_sigaction,NULL); sigaction(SIGSEGV,&custom_sigaction,NULL);
sigaction(SIGPIPE,&custom_sigaction,NULL); sigaction(SIGPIPE,&custom_sigaction,NULL);
sigaction(SIGPROF,&profiling_sigaction,NULL);
} }
void clear_environment(void) void clear_environment(void)
@ -41,6 +58,9 @@ void run(void)
if(callframe == F) if(callframe == F)
{ {
callframe = cpop(); callframe = cpop();
#ifdef EXTRA_CALL_INFO
cpop();
#endif
continue; continue;
} }
@ -65,29 +85,20 @@ void undefined()
} }
/* XT of compound definitions */ /* XT of compound definitions */
void call() void docol(void)
{ {
/* tail call optimization */ call(executing->parameter);
if(callframe != F)
cpush(callframe);
/* the parameter is the colon def */
callframe = executing->parameter;
} }
void primitive_execute(void) void primitive_execute(void)
{ {
WORD* word = untag_word(dpop()); executing = untag_word(dpop());
executing = word;
EXECUTE(executing); EXECUTE(executing);
} }
void primitive_call(void) void primitive_call(void)
{ {
CELL calling = dpop(); call(dpop());
if(callframe != F)
cpush(callframe);
callframe = calling;
} }
void primitive_ifte(void) void primitive_ifte(void)
@ -95,10 +106,7 @@ void primitive_ifte(void)
CELL f = dpop(); CELL f = dpop();
CELL t = dpop(); CELL t = dpop();
CELL cond = dpop(); CELL cond = dpop();
CELL calling = (untag_boolean(cond) ? t : f); call(untag_boolean(cond) ? t : f);
if(callframe != F)
cpush(callframe);
callframe = calling;
} }
void primitive_getenv(void) void primitive_getenv(void)
@ -117,3 +125,29 @@ void primitive_setenv(void)
range_error(F,e,USER_ENV); range_error(F,e,USER_ENV);
userenv[e] = value; userenv[e] = value;
} }
void primitive_profiling(void)
{
#ifndef EXTRA_CALL_INFO
general_error(PROFILING_DISABLED,F);
#else
CELL d = dpop();
if(d == F)
{
timerclear(&prof_timer.it_interval);
timerclear(&prof_timer.it_value);
}
else
{
prof_timer.it_interval.tv_sec = 0;
prof_timer.it_interval.tv_usec = 1000;
prof_timer.it_value.tv_sec = 0;
prof_timer.it_value.tv_usec = 1000;
profile_depth = to_fixnum(d);
}
if(setitimer(ITIMER_PROF,&prof_timer,NULL) < 0)
io_error(__FUNCTION__);
#endif
}

View File

@ -12,6 +12,9 @@
#define RUNQUEUE_ENV 9 /* used by library only */ #define RUNQUEUE_ENV 9 /* used by library only */
#define ARGS_ENV 10 #define ARGS_ENV 10
/* Profiling timer */
struct itimerval prof_timer;
/* Error handlers restore this */ /* Error handlers restore this */
sigjmp_buf toplevel; sigjmp_buf toplevel;
@ -36,8 +39,13 @@ WORD* executing;
/* TAGGED user environment data; see getenv/setenv prims */ /* TAGGED user environment data; see getenv/setenv prims */
CELL userenv[USER_ENV]; CELL userenv[USER_ENV];
void init_signals(void); /* Call stack depth to start profile counter from */
/* This ensures that words in the user's interpreter do not count */
CELL profile_depth;
void signal_handler(int signal, siginfo_t* siginfo, void* uap);
void profiling_step(int signal, siginfo_t* siginfo, void* uap);
void init_signals(void);
void clear_environment(void); void clear_environment(void);
INLINE CELL dpop(void) INLINE CELL dpop(void)
@ -79,9 +87,22 @@ INLINE CELL cpeek(void)
return get(cs - CELLS); return get(cs - CELLS);
} }
INLINE void call(CELL quot)
{
/* tail call optimization */
if(callframe != F)
{
#ifdef EXTRA_CALL_INFO
cpush(tag_word(executing));
#endif
cpush(callframe);
}
callframe = quot;
}
void run(void); void run(void);
void undefined(void); void undefined(void);
void call(void); void docol(void);
void primitive_execute(void); void primitive_execute(void);
void primitive_call(void); void primitive_call(void);
void primitive_ifte(void); void primitive_ifte(void);
@ -89,3 +110,4 @@ void primitive_getenv(void);
void primitive_setenv(void); void primitive_setenv(void);
void primitive_exit(void); void primitive_exit(void);
void primitive_os_env(void); void primitive_os_env(void);
void primitive_profiling(void);

View File

@ -1,24 +1,5 @@
#include "factor.h" #include "factor.h"
bool typep(CELL type, CELL tagged)
{
if(type < HEADER_TYPE)
{
if(TAG(tagged) == type)
return true;
}
else if(type >= HEADER_TYPE)
{
if(TAG(tagged) == OBJECT_TYPE)
{
if(untag_header(get(UNTAG(tagged))) == type)
return true;
}
}
return false;
}
CELL type_of(CELL tagged) CELL type_of(CELL tagged)
{ {
CELL tag = TAG(tagged); CELL tag = TAG(tagged);
@ -28,22 +9,14 @@ CELL type_of(CELL tagged)
return untag_header(get(UNTAG(tagged))); return untag_header(get(UNTAG(tagged)));
} }
void type_check(CELL type, CELL tagged) bool typep(CELL type, CELL tagged)
{ {
if(type < HEADER_TYPE) return type_of(tagged) == type;
{
if(TAG(tagged) == type)
return;
}
else if(type >= HEADER_TYPE)
{
if(TAG(tagged) == OBJECT_TYPE)
{
if(untag_header(get(UNTAG(tagged))) == type)
return;
}
} }
void type_check(CELL type, CELL tagged)
{
if(type_of(tagged) != type)
type_error(type,tagged); type_error(type,tagged);
} }

View File

@ -38,8 +38,8 @@ CELL T;
#define REAL_TYPE 102 /* RATIONAL or FLOAT */ #define REAL_TYPE 102 /* RATIONAL or FLOAT */
#define NUMBER_TYPE 103 /* COMPLEX or REAL */ #define NUMBER_TYPE 103 /* COMPLEX or REAL */
bool typep(CELL type, CELL tagged);
CELL type_of(CELL tagged); CELL type_of(CELL tagged);
bool typep(CELL type, CELL tagged);
void type_check(CELL type, CELL tagged); void type_check(CELL type, CELL tagged);
INLINE CELL tag_boolean(CELL untagged) INLINE CELL tag_boolean(CELL untagged)

View File

@ -7,6 +7,7 @@ WORD* word(CELL primitive, CELL parameter, CELL plist)
word->primitive = primitive; word->primitive = primitive;
word->parameter = parameter; word->parameter = parameter;
word->plist = plist; word->plist = plist;
word->call_count = 0;
return word; return word;
} }
@ -68,6 +69,17 @@ void primitive_set_word_plist(void)
word->plist = dpop(); word->plist = dpop();
} }
void primitive_word_call_count(void)
{
drepl(tag_fixnum(untag_word(dpeek())->call_count));
}
void primitive_set_word_call_count(void)
{
WORD* word = untag_word(dpop());
word->call_count = to_fixnum(dpop());
}
void fixup_word(WORD* word) void fixup_word(WORD* word)
{ {
word->xt = primitive_to_xt(word->primitive); word->xt = primitive_to_xt(word->primitive);

View File

@ -11,6 +11,8 @@ typedef struct {
CELL parameter; CELL parameter;
/* TAGGED property list for library code */ /* TAGGED property list for library code */
CELL plist; CELL plist;
/* UNTAGGED call count incremented by profiler */
CELL call_count;
} WORD; } WORD;
INLINE WORD* untag_word(CELL tagged) INLINE WORD* untag_word(CELL tagged)
@ -26,8 +28,6 @@ INLINE CELL tag_word(WORD* word)
WORD* word(CELL primitive, CELL parameter, CELL plist); WORD* word(CELL primitive, CELL parameter, CELL plist);
void update_xt(WORD* word); void update_xt(WORD* word);
void fixup_word(WORD* word);
void collect_word(WORD* word);
void primitive_wordp(void); void primitive_wordp(void);
void primitive_word(void); void primitive_word(void);
void primitive_word_primitive(void); void primitive_word_primitive(void);
@ -36,3 +36,7 @@ void primitive_word_parameter(void);
void primitive_set_word_parameter(void); void primitive_set_word_parameter(void);
void primitive_word_plist(void); void primitive_word_plist(void);
void primitive_set_word_plist(void); void primitive_set_word_plist(void);
void primitive_word_call_count(void);
void primitive_set_word_call_count(void);
void fixup_word(WORD* word);
void collect_word(WORD* word);