Index: kaffe/configure.in diff -u kaffe/configure.in:1.1.1.1 kaffe/configure.in:1.2 --- kaffe/configure.in:1.1.1.1 Thu May 4 16:12:55 2000 +++ kaffe/configure.in Thu May 4 16:51:12 2000 @@ -315,6 +315,39 @@ esac dnl ========================================================================= +dnl Allow support for profiling of C/jitted code +dnl ------------------------------------------------------------------------- + +AC_ARG_ENABLE(xprofiling,[ --enable-xprofiling Enable profiling for C and jitted code]) +case "$enable_xprofiling" in +"") ;; +no) ;; +*) CPPFLAGS="$CPPFLAGS -DKAFFE_XPROFILER";; +esac + +dnl ========================================================================= +dnl Allow support for debugging of C/jitted code +dnl ------------------------------------------------------------------------- + +AC_ARG_ENABLE(xdebugging,[ --enable-xdebugging Enable debugging symbol generation for jitted code]) +case "$enable_xdebugging" in +"") ;; +no) ;; +*) CPPFLAGS="$CPPFLAGS -DKAFFE_XDEBUGGING";; +esac + +dnl ========================================================================= +dnl Allow generation feedback data +dnl ------------------------------------------------------------------------- + +AC_ARG_ENABLE(feedback,[ --enable-feedback Enable generation of feedback data]) +case "$enable_feedback" in +"") ;; +no) ;; +*) CPPFLAGS="$CPPFLAGS -DKAFFE_FEEDBACK";; +esac + +dnl ========================================================================= dnl Allow enabling of profiling dnl ------------------------------------------------------------------------- @@ -323,6 +356,7 @@ AC_MSG_CHECKING(gprof(4) profiling support) if test x"${with_profiling}" = x"yes" ; then CFLAGS="$CFLAGS -pg" + CPPFLAGS="$CPPFLAGS -DKAFFE_CPROFILER" else with_profiling=no fi @@ -1096,6 +1130,7 @@ kaffe/kaffevm/systems/unix-pthreads/Makefile \ kaffe/kaffe/Makefile \ kaffe/kaffeh/Makefile \ +kaffe/xprof/Makefile \ kaffe/man/Makefile \ kaffe/scripts/Makefile \ kaffe/scripts/bat/Makefile \ @@ -1129,6 +1164,7 @@ libraries/javalib/rebuildLib \ kaffe/scripts/install-jar \ kaffe/scripts/kaffe \ +kaffe/scripts/kaffexprof \ kaffe/scripts/javac \ kaffe/scripts/javakey \ kaffe/scripts/jar \ Index: kaffe/FAQ/FAQ.feedback diff -u /dev/null kaffe/FAQ/FAQ.feedback:1.1 --- /dev/null Mon May 22 09:48:37 2000 +++ kaffe/FAQ/FAQ.feedback Thu May 4 16:51:45 2000 @@ -0,0 +1,99 @@ + +Documentation about the information feedback infrastructure +By the University of Utah Flux Group , +Mar 22, 2000 + +Introduction +------------ + +The information feedback infrastructure is a simple system that +provides a path for kaffe to feed information back into itself. For +example, previous runs of java code can provide information about what +methods are used so that in the future we can JIT these methods early +on to reduce call latency. The current system doesn't do much beyond +the previous example, but, more information and analysis can be added +to do more tricks. + + +Configure +--------- + +The feedback infrastructure can be turned on at configure time with +the --enable-feedback flag. Currently, the only feedback code +available is to record JIT'ed methods and loaded libraries and then +JIT/load them at startup time. + + +Usage +----- + +To pass kaffe a feedback file use the command line option `-Xfeedback' +and pass it the name of the file used to store feedback information. +If the file doesn't exist it will be created, otherwise Kaffe will +read in the previous information and store it in its internal data +structures. + +File Format: + + Normally, the feedback file is only manipulated by Kaffe, but its + just text so it can be manipulated between runs. A typical file is + broken into several sections by special directives that signal the + start and end of a section. Heres an examples: + + %begin jit-code java/util/StringTokenizer/hasMoreTokens()Z + precompile true + size 108 + address 0x8311248 + %end + + Every directive starts with a `%' at the beginning of the line + followed by the name of the directive and the arguments, so the + first line starts a new section of type `jit-code' and name + `java/util/StringTokenizer/hasMoreTokens()Z'. Following the begin + directive is a series of attributes and values for this section, + finally the section is terminated by the `%end' directive. The only + other directive is `%include' which will include and parse another, + similarly formatted file. Comments can also be added using the `#' + character followed by whatever text you please, the comment will run + until the end of the line. + + +Implementation +-------------- + +Added files: + + xprof/feedback.*: Primary interface for feedback code. + + xprof/sectionFile.*: Management functions for a flat file that is split + into sections. Feedback uses this module to store and read any + interesting bits of information for use between runs. The + functions try to keep the file pristine so that the user can edit + it, add comments, etc... without it constantly changing. + + xprof/fileSections.*: This contains the implementations for the sections + held by the file. The functions are responsible for processing + the values given in the file and building the internal structures. + +Modified Files: + + kaffe/main.c: Added `-Xfeedback' command line option which specifies + the feedback file to use. + + kaffevm/jit3/machine.c: Added feedback call to record methods that + have been JIT'ed. + + external.c: Added feedback call to record loaded dynamic libraries + +Control Flow: + + If `-Xfeedback' is specified on the command line then Kaffe will + build a section file structure and synchronize it with whatever file + was specified. As the system runs it can add/modify interesting + information within the section file structures. Finally, at exit + the feedback will be synchronized with the internal structures and + the file is ready to be used on the next run. + +Future: + + Add more feedback information and make better use of it... Index: kaffe/FAQ/FAQ.xdebugging diff -u /dev/null kaffe/FAQ/FAQ.xdebugging:1.1 --- /dev/null Mon May 22 09:48:37 2000 +++ kaffe/FAQ/FAQ.xdebugging Thu May 4 16:51:46 2000 @@ -0,0 +1,113 @@ + +Documentation about cross language debugging support for Kaffe's JIT +By the University of Utah Flux Group , +Mar 22, 2000 + +Introduction +------------ + +The cross language debugging support is basically an offshoot of the +xprofiler code. Since the profiler already generated symbols for the +JIT'ed code it seemed easy enough to also generate `.stabs' debugging +information for gdb. The result allows the gdb to report the file and +line number of the current location in the code, however, the +implementation has several limitations: + + - Only line number debugging information is generated, variable + locations and types aren't handled yet. + + - The debugging information is generated at runtime, so you need to + process the assembler output and manually load the symbol file + into gdb. This can turn into a real hassle and makes its harder + to do other things (e.g. breakpoints). + + - The java symbol names are mangled and only seem to be deciphered by + gdb 4.18, and even then it doesn't always work. + + - You have to manually add the source directories since I can't + figure out where to get them from the class structure. + + - JNI stubs, and other bits of code don't have debugging information + attached, so no useful information can be reported. Although, + this shouldn't be very hard to add. + + - There may be some problems with backtracing not working properly, + i don't know why though... + +Configure +--------- + +The xdebugging code can be enabled by using the --enable-xdebugging +flag when running configure. This will add a command line option for +specifying the debugging output file and enable code for translating +java debugging information to assembler `.stabs' directives. + + +Usage +----- + +The xdebugging output isn't generated unless the `-Xxdebug_file' flag +is specified on the command line. The flag takes one argument, the +name of the generated assembler file. The generated file will have +symbols for JIT'ed functions and whatever line debugging information +that was taken from the java debugging information. + +Example: + + > setenv KAFFE_DEBUG gdb + > java -Xxdebug_file HelloWorld.s HelloWorld + + Copyright 1998 Free Software Foundation, Inc. + GDB is free software, covered by the GNU General Public License, and you are + welcome to change it and/or distribute copies of it under certain conditions. + Type "show copying" to see the conditions. + There is absolutely no warranty for GDB. Type "show warranty" for details. + This GDB was configured as "i386-redhat-linux". + (gdb) + + + + [From another terminal] + > as HelloWorld.s -o HelloWorld.o + + [Back in gdb terminal] + (gdb) add-symbol-file HelloWorld.o 0 + add symbol table from file "nm.o" at text_addr = 0x0? + (y or n) y + (gdb) bt + + #0 0x8008329 in HelloWorld.main() at HelloWorld.java:12 + ... + + +Implementation +-------------- + +Added Files: + + xprof/debugFile.*: Does the majority of the work in generating the + assembler file with the debugging symbols. + + xprof/mangle.*: Mangles the java method names into a gnu style + format. + +Modified Files: + + kaffe/main.c: Added `-Xxdebug_file' command line arg + + kaffevm/jit3/machine.c: Added code to generate debugging information + for JIT'ed methods that had java debugging information. + +Control Flow: + + When the jitter finishes processing a method and installs it, it + will convert the java line debugging information from byte code + references to native code references. The xdebugging code simply + takes these converted references and uses debugFile functions to + generate the assembler directives. + +Future: + + Add more debugging information, types and variable locations. + + Automate the process of getting the information into gdb. Index: kaffe/FAQ/FAQ.xprofiler diff -u /dev/null kaffe/FAQ/FAQ.xprofiler:1.1 --- /dev/null Mon May 22 09:48:37 2000 +++ kaffe/FAQ/FAQ.xprofiler Thu May 4 16:51:48 2000 @@ -0,0 +1,267 @@ + +Documentation about cross language profiling for Kaffe's JIT +By the University of Utah Flux Group , +Mar 22, 2000 + +Introduction +------------ + +The cross language profiling facilities in Kaffe allow it to generate +profiling data which covers the native C VM code and the JIT'ed code. +Basically, it works just like regular C/UNIX profiling except we have +to generate the function symbols for JIT'ed code at runtime so that +gprof can tell where things are. The system also provides some additional +functionality: + + - Profiling can be turned on and off from java and C easily + + - Profiled code doesn't have to be contiguous, the system can + monitor scattered code and generate separate gmon files for + segments that are not "close" to each other. + + - Profiling can be done in stages to separate different modes in + program execution. For example, the profiling data accumulated + during the intialization of kaffe and the java program can be + separated out from the real work done by the program. + +The system doesn't replace the previous profiling work, rather we +feel it is complementary since it is nowhere near as accurate as the +instruction counter timing. + + +Configure +--------- + +The xprofiler is a configure time option that is turned on by the +--enable-xprofiling flag. The --with-staticvm and --with-staticlib +flags also needs to be used, otherwise the C code in the shared +library won't be monitored. Also, the --with-profiling flag needs to +be specified in order to get call graph data for the C VM code. +Currently, it has only been tested with jit3 on FreeBSD and Linux on +x86 machines, hopefully, more platforms can be tested/supported in the +future. + + +Usage +----- + +To turn profiling on at runtime simply add the `-Xxprof' flag to the +command line. Once the program has finished it will generate two +files, by default, kaffe-jit-symbols.s and xgmon.out. The first file, +kaffe-jit-symbols.s, is an assembler file which defines the symbols +for any of the JIT generated code. The second file, xgmon.out, is a +GNU gprof file with time samples covering the Kaffe and JIT code. +Call graph data is also generated for the JIT code, but call graph +data for the C code in Kaffe is only included if the +`--with-profiling' flag was passed to configure. Unfortunately, you +can't pass these files directly to gprof for analysis, first you need +to process the assembler file and link it to the Kaffe executable to +get a symbol table that gprof can examine. In order to make this a +little easier there is a kaffexprof script which will take care of +these steps automatically. Unfortunately, the current gprof versions +don't support java name mangling so resulting output is sometimes C++, +otherwise its just the mangled name. + +Profiling command line args: + + -Xxprof: Turns on xprofiling with the default filenames. (Note: We + also turn off class GC since we can't have jit memory moving + around or changing since gprof will attribute time and calls + incorrectly if new code is put in the same place.) + + -Xxprof_syms : Specify a different file name for the generated + assembler file that contains the profiler symbols. (Default: + "kaffe-jit-symbols.s") + + -Xxprof_gmon : Specify a different base name for the generated + gmon files. Its actually possible for more than one gmon file to + be produced, either by the java program using multiple profile + stages or there were files produced for code chunks that were + very far apart. If there were multiple stages then the name of + the gmon file is with the name of the stage appended to + it. If extra files are from distant discontiguous regions than + the first file is named and covers the first chunk of + memory, the rest of the files include the starting address for + the sample space appended to the name. (Default: "xgmon.out") + + +Example usage (first without kaffexprof): + + java -Xxprof HelloWorld + as kaffe-jit-symbols.s -o kaffe-jit-symbols.o + ld /usr/local/libexec/Kaffe kaffe-jit-symbols.o -o kaffe-jit-symbols + gprof kaffe-jit-symbols xgmon.out + + or: + + java -Xxprof HelloWorld + kaffexprof kaffe-jit-symbols xgmon.out + + +Java Interface: + + A simple java interface to the profiler is provided by the + kaffe.management.XProfiler class. This class provides three + functions for turning profiling accounting on and off, and starting + a new profiling stage: + + on() - Turn on profile accounting, basically, just record any data + from the monitors. (This is the default) + off() - Turn off profile accounting. Any functions that are JIT'ed + after this call will have monitors attached, its just that + nothing is recorded during this time. + stage(String name) - Start a new stage, the given name is used to + name the previous stage. This causes the current profiling + data to be output to a gmon file with `.name' appened to the + base gmon file name. Once the data has been written out + the internal counters are reset to zero and profiling for + the new stage continues. + + +Implementation +-------------- + +Added Files: + + xprof/xprofiler.*: The primary interface to the profiler. Kaffe + code should use these functions which then get routed to the + actual implementations below. + + xprof/gmonFile.*: Support code for writing gmon files + + xprof/mangle.*: Support code for mangling the method name into a gnu + style format. + + xprof/callGraph.*: Support code for tracking call arcs. Its + works similarly to mcount and other gmon code. + + xprof/memorySamples.*: Support code for tracking how often + the PC is at an address during the sampling period. It works + by building a tree that covers all "observed" areas of memory. + The tree is structured so that it can be walked by splitting the + sampled address into chunks and then indexing each branch in the + tree by the chunk value and then repeating for the next + chunk/level in the tree. Its not as fast as a flat array, but + its more flexible and doesn't eat a lot of memory. + + xprof/debugFile.*: Support code for generating assembler files + with symbol information. Unfortunately, the symbols are mangled + since the assembler doesn't like the funky java method names and + signatures. + + xprof/gmon_out.h: This was lifted from gprof, we just need it for + the structures that define the file format. + + scripts/kaffexprof.in: Shell script that takes the base name for the + generated assembler file and the gmon file and then builds a + usable executable with all the symbols and runs gprof. + + scripts/nm2as.awk: This is used by kaffexprof to produce an + assembler file with symbols corresponding to the ones in the + object file that is specified. We use this to get around problems + when trying to link Kaffe with the generated JIT symbol files, + basically LD would move some symbols around and screw up the + output. With this hack we get all the symbols in the right place. + +Modified Files: + + config/i386/jit3-i386.def: Modified prologue to call mcount like + function in xprofiler. + + config/i386/*/md.h: Added defines for signal handler prototypes and + macro to get the PC out of the sigcontext. + + kaffevm/systems/unix-jthreads/jthread.c: Added code to Turn off the + profile timer initially set up and just use the time slicing timer + used for threads. Added code to the interrupt handler to report + the PC seen in the interrupt. + + kaffevm/jit3/machine.c: Added calls to xprofiler to inform it of new + JIT'ed code and symbols. + + kaffe/main.c: Added new command line args and their handlers. + + kaffevm/classMethod.c: Added test to stop the free'ing of JIT'ed + class initializer code so that the generated symbols would + still be valid. + +Control Flow: + + The profiler is started immediately after the JVM is initialized, + unfortunately, we have to wait this long since the code allocates + memory using the GC. However, if `--with-profiling' flag is + specified in configure, we can copy out the profiling data collected + during initialization into our own sample counters, so we don't lose + the information. Depending on the threading system used, a PC + sampler needs to be installed which samples the PC at 10ms + intervals. The samples are recorded using the memorySamples code + and any JIT'ed code is added to the set of "observed" memory + regions, which is initially just the kaffe code and libraries. + Any PCs that fall out of the "observed" region are recorded as + misses and attributed to the "_unaccounted_" symbol (Unobserved + regions can most likely be attributed to dynamically loaded + libraries). Call graph data is accumulated by placing a call to the + profileArcHit function in the prologue of every JIT'ed function. + All of this data is then written out by an atexit() function or when + theres a transition to a new stage. + +System dependencies: + + A call to profileArcHit needs to be added to the prologue of JIT'ed + methods. + + For unix-jthreads: + + - SIGNAL_ARGS(sig, sc) needs to be defined to specify the + arguments of the prototype for a signal handler. + + - SIGNAL_CONTEXT_POINTER(sc) needs to be defined to something that + can be used in a parameter list or declaration to define a + pointer to a signal context pointer (struct sigcontext, + siginfo_t, whatever) + + - GET_SIGNAL_CONTEXT_POINTER(sc) needs to be defined to return a + pointer to the signal context + + - SIGNAL_PC(sc) needs to be defined to return the PC value from the + given signal context + + - _KAFFE_OVERRIDE_MCOUNT_DEF needs to be defined in order to override + the libc `mcount'. This doesn't need to be defined, its only useful + if `--with-profiling' is specified and you care about getting call + data about native functions. The standard `mcount' won't work properly + with native interfaces since the return caller address will be in the + heap and not in the text section as it expects, so the calls are + ignored. + + - _KAFFE_OVERRIDE_MCOUNT needs to be defined if MCOUNT_DEF is a static + inline and this function is used to call it. + + An appropriate PC sampler needs to be installed. Initially, the + ITIMER_PROF timer was installed and always running, but this was + caused problems with unix-jthreads. It turned out that the + time-slicing interrupt would occur at almost the same time as the PC + sampler, resulting in the majority of the samples being in the + time-slicing code, instead of the more interesting java code. The + solution was simply to incorporate sampling the PC into the time + slicing code. Of course, systems that don't use unix-jthreads or + have unforseen quirks will need to find their own way to take samples. + + The debugFile code generates an assembler file with a number of + directives in it, if a non GNU assembler is being used then these + will probably need to be changed. However, the file doesn't contain + any assembly instructions so it should be somewhat independent of + the architecture. + + None of this has been tested on a 64 bit system... + +Future: + + Possibly add support for profiling the interpreter, unfortunately, + it isn't clear how to do this effectively... + + Profile shared libraries, the current system can support this, the + only problem is that its hard to determine the upper and lower bound + of the libraries text segment in order to do monitoring. + + Support for more platforms. Index: kaffe/config/i386/jit3-i386.def diff -u kaffe/config/i386/jit3-i386.def:1.1.1.1 kaffe/config/i386/jit3-i386.def:1.3 --- kaffe/config/i386/jit3-i386.def:1.1.1.1 Thu May 4 16:12:58 2000 +++ kaffe/config/i386/jit3-i386.def Fri May 5 09:30:44 2000 @@ -4,6 +4,10 @@ * Copyright (c) 1996, 1997 * Transvirtual Technologies, Inc. All rights reserved. * + * Cross-language profiling changes contributed by + * the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + * * See the file "license.terms" for information on usage and redistribution * of this file. */ @@ -11,6 +15,7 @@ #include "debug.h" #include "classMethod.h" #include "md.h" +#include "xprofiler.h" #if defined(TRACE_METHOD_END) && defined(__GNUC__) asm(".globl tStore\ntStore: .long 0,0"); @@ -178,6 +183,48 @@ define_insn(prologue, prologue_xxx) { label* l; + +#if defined(KAFFE_XPROFILER) + if (xProfFlag) + { + /* Store the pointer to this function in eax */ + OUT = 0xB8|REG_eax; + l = (label*)newLabel(); + l->type = Llong|Linternal|Labsolute; + l->to = 0; + l->at = (uintp)CODEPC; + LOUT = 0; + debug(("movl #?,eax\n")); + + /* Push second arg to profileArcHit */ + OUT = 0x50|REG_eax; + debug(("pushl eax\n")); + + /* Get return address from the stack */ + OUT = 0x8B; + OUT = (REG_eax<<3)|0x44; + OUT = 0x24; + OUT = 0x04; + debug(("movl eax,4(esp)\n")); + + /* Push first arg to profileArcHit */ + OUT = 0x50|REG_eax; + debug(("pushl eax\n")); + /* Load the address to profileArcHit */ + OUT = 0xB8|REG_eax; + LOUT = (int)profileArcHit; + debug(("movl profileArcHit,eax\n")); + /* Make the call */ + OUT = 0xFF; + OUT = 0xD0|REG_eax; + debug(("call profileArcHit\n")); + /* Pop the args off */ + OUT = 0x58|REG_eax; + debug(("popl eax\n")); + OUT = 0x58|REG_eax; + debug(("popl eax\n")); + } +#endif OUT = 0x50|REG_ebp; OUT = 0x89; Index: kaffe/config/i386/freebsd2/md.h diff -u kaffe/config/i386/freebsd2/md.h:1.1.1.1 kaffe/config/i386/freebsd2/md.h:1.2 --- kaffe/config/i386/freebsd2/md.h:1.1.1.1 Thu May 4 16:12:59 2000 +++ kaffe/config/i386/freebsd2/md.h Thu May 4 16:52:06 2000 @@ -32,6 +32,39 @@ #define LOAD_FP(fdata) \ asm("frstor %0": :"m"(*fdata)) +/* Define signal context macros for xprofiling */ +#define SIGNAL_ARGS(sig, sc) int sig, int __code, struct sigcontext *##sc +#define SIGNAL_CONTEXT_POINTER(scp) struct sigcontext *##scp +#define GET_SIGNAL_CONTEXT_POINTER(sc) (sc) +#define SIGNAL_PC(scp) (scp)->sc_pc + +int kaffeStdProfRate(void); +#define KAFFE_STD_PROF_RATE \ +int kaffeStdProfRate(void) \ +{ \ + struct clockinfo clockinfo; \ + int retval = 0; \ + size_t size; \ + int mib[2]; \ + \ + size = sizeof(clockinfo); \ + mib[0] = CTL_KERN; \ + mib[1] = KERN_CLOCKRATE; \ + if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) < 0) { \ + } else if (clockinfo.profhz == 0) { \ + retval = clockinfo.hz; \ + } else { \ + retval = clockinfo.profhz; \ + } \ + return( retval ); \ +} + +/* Override the standard mcount with the xprofiling one */ +#include + +#define _KAFFE_OVERRIDE_MCOUNT_DEF _MCOUNT_DECL __P((uintfptr_t frompc, uintfptr_t selfpc)) +#define _KAFFE_OVERRIDE_MCOUNT MCOUNT + #if defined(TRANSLATOR) #include "jit-md.h" #endif Index: kaffe/config/i386/linux/md.h diff -u kaffe/config/i386/linux/md.h:1.1.1.1 kaffe/config/i386/linux/md.h:1.2 --- kaffe/config/i386/linux/md.h:1.1.1.1 Thu May 4 16:12:59 2000 +++ kaffe/config/i386/linux/md.h Thu May 4 16:52:10 2000 @@ -23,4 +23,9 @@ extern void init_md(void); #define INIT_MD() init_md() +#define SIGNAL_ARGS(sig, sc) int sig, struct sigcontext *##sc +#define SIGNAL_CONTEXT_POINTER(sc) struct sigcontext *##sc +#define GET_SIGNAL_CONTEXT_POINTER(sc) (&sc) +#define SIGNAL_PC(scp) scp->eip + #endif Index: kaffe/include/Makefile.am diff -u kaffe/include/Makefile.am:1.1.1.1 kaffe/include/Makefile.am:1.2 --- kaffe/include/Makefile.am:1.1.1.1 Thu May 4 16:13:05 2000 +++ kaffe/include/Makefile.am Thu May 4 16:52:14 2000 @@ -83,6 +83,7 @@ kaffe_lang_UNIXProcess.h \ kaffe_management_JIT.h \ kaffe_management_Debug.h \ + kaffe_management_XProfiler.h \ kaffe_security_provider_MD2.h \ kaffe_security_provider_MD4.h \ kaffe_security_provider_MD5.h \ Index: kaffe/kaffe/Makefile.am diff -u kaffe/kaffe/Makefile.am:1.1.1.1 kaffe/kaffe/Makefile.am:1.2 --- kaffe/kaffe/Makefile.am:1.1.1.1 Thu May 4 16:13:06 2000 +++ kaffe/kaffe/Makefile.am Thu May 4 16:52:24 2000 @@ -6,4 +6,4 @@ # See the file "license.terms" for information on usage and redistribution # of this file. -SUBDIRS = kaffeh kaffevm kaffe scripts man +SUBDIRS = kaffeh kaffevm xprof kaffe scripts man Index: kaffe/kaffe/kaffe/Makefile.am diff -u kaffe/kaffe/kaffe/Makefile.am:1.1.1.1 kaffe/kaffe/kaffe/Makefile.am:1.2 --- kaffe/kaffe/kaffe/Makefile.am:1.1.1.1 Thu May 4 16:13:06 2000 +++ kaffe/kaffe/kaffe/Makefile.am Thu May 4 16:52:49 2000 @@ -8,11 +8,12 @@ libexec_PROGRAMS = Kaffe -INCLUDES = -I../kaffevm -I$(srcdir)/../kaffevm -I$(top_srcdir)/libltdl +INCLUDES = -I../kaffevm -I$(srcdir)/../kaffevm -I$(top_srcdir)/libltdl \ + -I$(top_srcdir)/kaffe/xprof Kaffe_SOURCES = main.c version.c -LIBKAFFEVM = ../kaffevm/libkaffevm.la +LIBKAFFEVM = ../kaffevm/libkaffevm.la ../xprof/libkaffexprof.la Kaffe_LDFLAGS = -export-dynamic Kaffe_LDADD = $(DLOPEN_JAVA_LIBS) $(LIBKAFFEVM) $(KAFFE_LIBS) Index: kaffe/kaffe/kaffe/main.c diff -u kaffe/kaffe/kaffe/main.c:1.1.1.2 kaffe/kaffe/kaffe/main.c:1.4 --- kaffe/kaffe/kaffe/main.c:1.1.1.2 Tue May 16 17:03:46 2000 +++ kaffe/kaffe/kaffe/main.c Tue May 16 17:12:29 2000 @@ -5,6 +5,10 @@ * Copyright (c) 1996-2000 * Transvirtual Technologies, Inc. All rights reserved. * + * Cross-language profiling changes contributed by + * the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + * * See the file "license.terms" for information on usage and redistribution * of this file. */ @@ -23,11 +27,20 @@ #include "md.h" #include "ltdl.h" #include "version.h" +#include "debugFile.h" +#include "xprofiler.h" +#include "fileSections.h" +#include "feedback.h" +#include "methodCache.h" #if defined(KAFFE_PROFILER) extern int profFlag; #endif +extern char* engine_name; +extern char* engine_version; +static char* java_version; + #include "jni.h" JavaVMInitArgs vmargs; @@ -101,6 +114,19 @@ farg = options(argv); argc = argc - farg; +#if defined(KAFFE_XPROFILER) + if( xProfFlag ) + { + if( !enableXCallGraph() ) + { + fprintf(stderr, + "Unable to initialize cross " + "language profiling\n"); + xProfFlag = 0; + } + } +#endif + /* Get the class name to start with */ if (argv[farg] == 0) { usage(); @@ -391,6 +417,69 @@ vmargs.enableClassGC = 0; } #endif +#if defined(KAFFE_XPROFILER) + else if (strcmp(argv[i], "-Xxprof") == 0) { + xProfFlag = 1; + vmargs.enableClassGC = 0; + } + else if (strcmp(argv[i], "-Xxprof_syms") == 0) { + i++; + if (argv[i] == 0) { + fprintf(stderr, + "Error: -Xxprof_syms option requires " + "a file name.\n"); + } + else if( !profileSymbolFile(argv[i]) ) + { + fprintf(stderr, + "Unable to create profiler symbol " + "file %s.\n", + argv[i]); + } + } + else if (strcmp(argv[i], "-Xxprof_gmon") == 0) { + i++; + if (argv[i] == 0) { + fprintf(stderr, + "Error: -Xxprof_gmon option requires " + "a file name.\n"); + } + else if (!profileGmonFile(argv[i])) + { + fprintf(stderr, + "Unable to create gmon file %s.\n", + argv[i]); + } + } +#endif +#if defined(KAFFE_XDEBUGGING) + else if (strcmp(argv[i], "-Xxdebug_file") == 0) { + i++; + if (argv[i] == 0) { + fprintf(stderr, + "Error: -Xxdebug_file option requires " + "a file name.\n"); + } + else + { + machine_debug_filename = argv[i]; + } + } +#endif +#if defined(KAFFE_FEEDBACK) + else if (strcmp(argv[i], "-Xfeedback") == 0) { + i++; + if (argv[i] == 0) { + fprintf(stderr, + "Error: -Xfeedback option requires a " + "file name.\n"); + } + else + { + feedback_filename = argv[i]; + } + } +#endif else if (strcmp(argv[i], "-nodeadlock") == 0) { deadlockDetection = 0; } @@ -437,6 +526,12 @@ prop->key = &argv[i][2]; prop->value = &argv[i][sz]; } + else if (argv[i][1] == 'X') { + fprintf(stderr, + "Error: Unrecognized JVM specific option " + "`%s'.\n", + argv[i]); + } /* The following options are not supported and will be * ignored for compatibility purposes. */ @@ -485,6 +580,17 @@ fprintf(stderr, " -nodeadlock Disable deadlock detection\n"); #if defined(KAFFE_PROFILER) fprintf(stderr, " -prof Enable profiling of Java methods\n"); +#endif +#if defined(KAFFE_XPROFILER) + fprintf(stderr, " -Xxprof Enable cross language profiling\n"); + fprintf(stderr, " -Xxprof_syms Name of the profiling symbols file [Default: kaffe-jit-symbols.s]\n"); + fprintf(stderr, " -Xxprof_gmon Base name for gmon files [Default: xgmon.out]\n"); +#endif +#if defined(KAFFE_XDEBUGGING) + fprintf(stderr, " -Xxdebug_file Name of the debugging symbols file\n"); +#endif +#if defined(KAFFE_FEEDBACK) + fprintf(stderr, " -Xfeedback The file name to write feedback data too\n"); #endif fprintf(stderr, " -debug * Trace method calls\n"); fprintf(stderr, " -noasyncgc * Do not garbage collect asynchronously\n"); Index: kaffe/kaffe/kaffevm/Makefile.am diff -u kaffe/kaffe/kaffevm/Makefile.am:1.1.1.1 kaffe/kaffe/kaffevm/Makefile.am:1.2 --- kaffe/kaffe/kaffevm/Makefile.am:1.1.1.1 Thu May 4 16:13:06 2000 +++ kaffe/kaffe/kaffevm/Makefile.am Thu May 4 17:00:14 2000 @@ -21,7 +21,7 @@ @engine_frag@ -INCLUDES = -I$(top_srcdir)/kaffe/kaffevm/$(THREAD_DIR) $(ENGINE_INCLUDES) -I$(top_srcdir)/libltdl $(ENGINE_DEFS) +INCLUDES = -I$(top_srcdir)/kaffe/kaffevm/$(THREAD_DIR) $(ENGINE_INCLUDES) -I$(top_srcdir)/libltdl $(ENGINE_DEFS) -I$(top_srcdir)/kaffe/xprof lib_LTLIBRARIES = libkaffevm.la Index: kaffe/kaffe/kaffevm/baseClasses.c diff -u kaffe/kaffe/kaffevm/baseClasses.c:1.1.1.1 kaffe/kaffe/kaffevm/baseClasses.c:1.2 --- kaffe/kaffe/kaffevm/baseClasses.c:1.1.1.1 Thu May 4 16:13:06 2000 +++ kaffe/kaffe/kaffevm/baseClasses.c Thu May 4 17:00:39 2000 @@ -34,6 +34,10 @@ #include "md.h" #include "java_lang_Cloneable.h" #include "gcj/gcj.h" +#include "xprofiler.h" +#include "feedback.h" +#include "debugFile.h" +#include "fileSections.h" Utf8Const* init_name; Utf8Const* final_name; @@ -107,6 +111,24 @@ main_collector = initCollector(); GC_init(main_collector); +#if defined(KAFFE_XPROFILER) + /* Start up the profiler here so we can cover init stuff */ + if( xProfFlag ) + { + if( !enableXProfiling() ) + { + fprintf(stderr, + "Unable to initialize cross " + "language profiling\n"); + } + } +#endif +#if defined(KAFFE_XDEBUGGING) + if( machine_debug_filename ) + { + machine_debug_file = createDebugFile(machine_debug_filename); + } +#endif threadStackSize = Kaffe_JavaVMArgs[0].nativeStackSize; if (threadStackSize == 0) { @@ -126,6 +148,19 @@ /* Init native support */ initNative(); +#if defined(KAFFE_FEEDBACK) + /* Install any file sections used by feedback */ + installFileSections(); + if( feedback_filename ) + { + if( feedbackFile(feedback_filename) ) + { + syncFeedback(); + processFeedback(); + } + } +#endif + /* Create the initialise and finalize names and signatures. */ init_name = utf8ConstNew(INIT, -1); final_name = utf8ConstNew(FINAL, -1); Index: kaffe/kaffe/kaffevm/classMethod.c diff -u kaffe/kaffe/kaffevm/classMethod.c:1.1.1.1 kaffe/kaffe/kaffevm/classMethod.c:1.2 --- kaffe/kaffe/kaffevm/classMethod.c:1.1.1.1 Thu May 4 16:13:06 2000 +++ kaffe/kaffe/kaffevm/classMethod.c Thu May 4 17:00:42 2000 @@ -42,6 +42,7 @@ #include "jni.h" #include "methodCache.h" #include "gcj/gcj.h" +#include "xprofiler.h" #if 0 #define METHOD_TRUE_NCODE(METH) (METH)->c.ncode.ncode_start @@ -496,10 +497,15 @@ makeMethodInactive(meth); #endif #endif - METHOD_NATIVECODE(meth) = 0; - KFREE(meth->c.ncode.ncode_start); - meth->c.ncode.ncode_start = 0; - meth->c.ncode.ncode_end = 0; +#if defined(KAFFE_XPROFILER) + if( !xProfFlag ) +#endif + { + METHOD_NATIVECODE(meth) = 0; + KFREE(meth->c.ncode.ncode_start); + meth->c.ncode.ncode_start = 0; + meth->c.ncode.ncode_end = 0; + } } done: Index: kaffe/kaffe/kaffevm/external.c diff -u kaffe/kaffe/kaffevm/external.c:1.1.1.2 kaffe/kaffe/kaffevm/external.c:1.3 --- kaffe/kaffe/kaffevm/external.c:1.1.1.2 Tue May 16 17:03:50 2000 +++ kaffe/kaffe/kaffevm/external.c Tue May 16 17:12:33 2000 @@ -32,6 +32,7 @@ #include "jsignal.h" #include "stats.h" #include "ltdl.h" +#include "feedback.h" #ifndef STUB_PREFIX #define STUB_PREFIX "" @@ -173,13 +174,22 @@ EXIT(1); } +int +loadNativeLibrary(char* lib, char *errbuf, size_t errsiz) +{ + int retval; + + retval = loadNativeLibrary2(lib, 1, errbuf, errsiz); + return( retval ); +} + /* * Link in a native library. If successful, returns an index >= 0 that * can be passed to unloadNativeLibrary(). Otherwise, returns -1 and * fills errbuf (if not NULL) with the error message. Assumes synchronization. */ int -loadNativeLibrary(char* path, char *errbuf, size_t errsiz) +loadNativeLibrary2(char* path, int default_refs, char *errbuf, size_t errsiz) { struct _libHandle *lib; int index; @@ -242,7 +252,7 @@ return -1; } - lib->ref = 1; + lib->ref = default_refs; lib->name = KMALLOC(strlen(path) + 1); addToCounter(<mem, "vmmem-libltdl", 1, GCSIZEOF(lib->name)); strcpy(lib->name, path); @@ -252,6 +262,9 @@ "\tLOAD desc=%p index=%d ++ref=%d\n", lib->name, lib->desc, index, lib->ref); ) +#if defined(KAFFE_FEEDBACK) + feedbackLibrary(path, true); +#endif return index; } Index: kaffe/kaffe/kaffevm/external.h diff -u kaffe/kaffe/kaffevm/external.h:1.1.1.1 kaffe/kaffe/kaffevm/external.h:1.2 --- kaffe/kaffe/kaffevm/external.h:1.1.1.1 Thu May 4 16:13:06 2000 +++ kaffe/kaffe/kaffevm/external.h Thu May 4 17:00:50 2000 @@ -27,6 +27,7 @@ void initNative(void); int loadNativeLibrary(char*, char*, size_t); +int loadNativeLibrary2(char*, int, char*, size_t); void unloadNativeLibrary(int); void* loadNativeLibrarySym(char*); bool native(struct _methods*, struct _errorInfo*); Index: kaffe/kaffe/kaffevm/findInJar.c diff -u kaffe/kaffe/kaffevm/findInJar.c:1.1.1.2 kaffe/kaffe/kaffevm/findInJar.c:1.3 --- kaffe/kaffe/kaffevm/findInJar.c:1.1.1.2 Tue May 16 17:03:51 2000 +++ kaffe/kaffe/kaffevm/findInJar.c Tue May 16 17:12:37 2000 @@ -40,6 +40,8 @@ classpathEntry* classpath; +static iLock* classpathLock; + char* realClassPath; void initClasspath(void); @@ -167,7 +169,6 @@ classFile hand; ssize_t j; jarEntry* entry; - static iLock* jarlock; classpathEntry* ptr; int i; int rc; @@ -177,8 +178,8 @@ DBG(CLASSLOOKUP, dprintf("Scanning for element %s\n", cname); ) - /* One into the jar at once */ - lockStaticMutex(&jarlock); + /* Stop anyone from modifying the class path while we're working */ + lockStaticMutex(&classpathLock); for (ptr = classpath; ptr != 0; ptr = ptr->next) { hand.type = ptr->type; @@ -286,7 +287,7 @@ postNoClassDefFoundError(einfo, cname); done:; - unlockStaticMutex(&jarlock); + unlockStaticMutex(&classpathLock); return (hand); } @@ -443,6 +444,7 @@ { classpathEntry* ptr; classpathEntry* lptr; + int iLockRoot; DBG(INITCLASSPATH, dprintf("insertClasspath(): '%s' %spend\n", cp, prepend ? "pre" : "ap"); ) @@ -451,9 +453,11 @@ return; lptr = 0; + lockStaticMutex(&classpathLock); for (ptr = classpath; ptr != 0; ptr = ptr->next) { if (strcmp(ptr->path, cp) == 0) { /* Already in */ + unlockStaticMutex(&classpathLock); return (0); } lptr = ptr; @@ -472,6 +476,7 @@ ptr->next = 0; lptr->next = ptr; } + unlockStaticMutex(&classpathLock); return(1); } Index: kaffe/kaffe/kaffevm/jni.c diff -u kaffe/kaffe/kaffevm/jni.c:1.1.1.2 kaffe/kaffe/kaffevm/jni.c:1.3 --- kaffe/kaffe/kaffevm/jni.c:1.1.1.2 Tue May 16 17:03:53 2000 +++ kaffe/kaffe/kaffevm/jni.c Tue May 16 17:12:42 2000 @@ -46,6 +46,7 @@ #include "basecode.h" #include "icode.h" #include "machine.h" +#include "feedback.h" #endif /* @@ -3632,6 +3633,10 @@ count = sizeofSigMethod(xmeth, false); count += 1 - isStatic; +#if defined(KAFFE_FEEDBACK) + if( kaffe_feedback_file ) + lockMutex(kaffe_feedback_file); +#endif /* Construct a wrapper to call the JNI method with the correct * arguments. */ @@ -3951,6 +3956,10 @@ #endif leaveTranslator(); +#if defined(KAFFE_FEEDBACK) + if( kaffe_feedback_file ) + unlockMutex(kaffe_feedback_file); +#endif if (!success) { throwError(&info); } Index: kaffe/kaffe/kaffevm/methodCache.c diff -u kaffe/kaffe/kaffevm/methodCache.c:1.1.1.1 kaffe/kaffe/kaffevm/methodCache.c:1.2 --- kaffe/kaffe/kaffevm/methodCache.c:1.1.1.1 Thu May 4 16:13:07 2000 +++ kaffe/kaffe/kaffevm/methodCache.c Thu May 4 17:00:54 2000 @@ -171,6 +171,22 @@ } } +void +walkActiveMethods(void *arg, void (*walker)(void *arg, Method *meth)) +{ + int i; + + for(i = 0; i < METHCACHEHASHSZ; i++) { + methCacheEntry *entry = methCacheTable.hash[i]; + while (entry) { + if (entry->meth->class != 0) { + walker(arg, entry->meth); + } + entry = entry->next; + } + } +} + #if defined(DUMPMETHODCACHESTATS) /* * A function to dump the length of the lists in the method cache Index: kaffe/kaffe/kaffevm/methodCache.h diff -u kaffe/kaffe/kaffevm/methodCache.h:1.1.1.1 kaffe/kaffe/kaffevm/methodCache.h:1.2 --- kaffe/kaffe/kaffevm/methodCache.h:1.1.1.1 Thu May 4 16:13:07 2000 +++ kaffe/kaffe/kaffevm/methodCache.h Thu May 4 17:00:55 2000 @@ -18,5 +18,7 @@ extern bool makeMethodActive(Method* meth); extern void makeMethodInactive(Method* meth); extern void dumpActiveMethods(jobject printstream, jobject loader); +extern void walkActiveMethods(void *arg, + void (*walker)(void *arg, Method *meth)); #endif /* __kaffevm_methodcache_h */ Index: kaffe/kaffe/kaffevm/stringSupport.h diff -u kaffe/kaffe/kaffevm/stringSupport.h:1.1.1.1 kaffe/kaffe/kaffevm/stringSupport.h:1.2 --- kaffe/kaffe/kaffevm/stringSupport.h:1.1.1.1 Thu May 4 16:13:08 2000 +++ kaffe/kaffe/kaffevm/stringSupport.h Thu May 4 17:00:57 2000 @@ -122,4 +122,31 @@ /* Initialize utf8const support system */ extern void utf8ConstInit(void); +/* + * Extract a character from a Java-style Utf8 string. + * PTR points to the current UTF-8 byte; END points to the end of the string. + * PTR is incremented to point after the character that gets returns. + * On an error, -1 is returned and PTR is no longer valid. + */ +#define UTF8_GET(PTR, END) \ + ((PTR) >= (END) \ + ? -1 \ + : (PTR)[0] == 0 \ + ? (PTR)++, -1 \ + : ((PTR)[0]&0x80) == 0 \ + ? *(PTR)++ \ + : ((PTR)+2)<=(END) \ + && ((PTR)[0]&0xE0) == 0xC0 \ + && ((PTR)[1]&0xC0) == 0x80 \ + && ((PTR)+=2, 1) \ + ? (((PTR)[-2] & 0x1F) << 6) + ((PTR)[-1] & 0x3F) \ + : ((PTR)+3)<=(END) \ + && ((PTR)[0] & 0xF0) == 0xE0 \ + && ((PTR)[1] & 0xC0) == 0x80 \ + && ((PTR)[2] & 0xC0) == 0x80 \ + && ((PTR)+=3, 1) \ + ? (((PTR)[-3]&0x1F) << 12) \ + + (((PTR)[-2]&0x3F) << 6) + ((PTR)[-1]&0x3F) \ + : -1) + #endif Index: kaffe/kaffe/kaffevm/utf8const.c diff -u kaffe/kaffe/kaffevm/utf8const.c:1.1.1.1 kaffe/kaffe/kaffevm/utf8const.c:1.2 --- kaffe/kaffe/kaffevm/utf8const.c:1.1.1.1 Thu May 4 16:13:08 2000 +++ kaffe/kaffe/kaffevm/utf8const.c Thu May 4 17:00:59 2000 @@ -41,33 +41,6 @@ #define hashRemove(t, x) (void)NULL #endif -/* - * Extract a character from a Java-style Utf8 string. - * PTR points to the current UTF-8 byte; END points to the end of the string. - * PTR is incremented to point after the character that gets returns. - * On an error, -1 is returned and PTR is no longer valid. - */ -#define UTF8_GET(PTR, END) \ - ((PTR) >= (END) \ - ? -1 \ - : (PTR)[0] == 0 \ - ? (PTR)++, -1 \ - : ((PTR)[0]&0x80) == 0 \ - ? *(PTR)++ \ - : ((PTR)+2)<=(END) \ - && ((PTR)[0]&0xE0) == 0xC0 \ - && ((PTR)[1]&0xC0) == 0x80 \ - && ((PTR)+=2, 1) \ - ? (((PTR)[-2] & 0x1F) << 6) + ((PTR)[-1] & 0x3F) \ - : ((PTR)+3)<=(END) \ - && ((PTR)[0] & 0xF0) == 0xE0 \ - && ((PTR)[1] & 0xC0) == 0x80 \ - && ((PTR)[2] & 0xC0) == 0x80 \ - && ((PTR)+=3, 1) \ - ? (((PTR)[-3]&0x1F) << 12) \ - + (((PTR)[-2]&0x3F) << 6) + ((PTR)[-1]&0x3F) \ - : -1) - /* Internal variables */ #ifndef KAFFEH /* Yuk! */ static hashtab_t hashTable; Index: kaffe/kaffe/kaffevm/jit3/Makefile.am diff -u kaffe/kaffe/kaffevm/jit3/Makefile.am:1.1.1.1 kaffe/kaffe/kaffevm/jit3/Makefile.am:1.2 --- kaffe/kaffe/kaffevm/jit3/Makefile.am:1.1.1.1 Thu May 4 16:13:09 2000 +++ kaffe/kaffe/kaffevm/jit3/Makefile.am Thu May 4 17:01:02 2000 @@ -8,7 +8,7 @@ @engine_frag@ -INCLUDES = -I$(top_srcdir)/kaffe/kaffevm/$(THREAD_DIR) -I.. -I$(srcdir)/.. $(ENGINE_DEFS) +INCLUDES = -I$(top_srcdir)/kaffe/kaffevm/$(THREAD_DIR) -I.. -I$(srcdir)/.. $(ENGINE_DEFS) -I$(top_srcdir)/kaffe/xprof noinst_LTLIBRARIES = libengine.la Index: kaffe/kaffe/kaffevm/jit3/Makefile.in diff -u kaffe/kaffe/kaffevm/jit3/Makefile.in:1.1.1.1 kaffe/kaffe/kaffevm/jit3/Makefile.in:1.2 --- kaffe/kaffe/kaffevm/jit3/Makefile.in:1.1.1.1 Thu May 4 16:13:09 2000 +++ kaffe/kaffe/kaffevm/jit3/Makefile.in Thu May 4 17:01:03 2000 @@ -134,7 +134,7 @@ # of this file. -INCLUDES = -I$(top_srcdir)/kaffe/kaffevm/$(THREAD_DIR) -I.. -I$(srcdir)/.. $(ENGINE_DEFS) +INCLUDES = -I$(top_srcdir)/kaffe/kaffevm/$(THREAD_DIR) -I.. -I$(srcdir)/.. $(ENGINE_DEFS) -I$(top_srcdir)/kaffe/xprof noinst_LTLIBRARIES = libengine.la Index: kaffe/kaffe/kaffevm/jit3/machine.c diff -u kaffe/kaffe/kaffevm/jit3/machine.c:1.1.1.1 kaffe/kaffe/kaffevm/jit3/machine.c:1.3 --- kaffe/kaffe/kaffevm/jit3/machine.c:1.1.1.1 Thu May 4 16:13:09 2000 +++ kaffe/kaffe/kaffevm/jit3/machine.c Fri May 5 09:30:53 2000 @@ -4,6 +4,10 @@ * Copyright (c) 1996-1999 * Transvirtual Technologies, Inc. All rights reserved. * + * Cross-language profiling changes contributed by + * the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + * * See the file "license.terms" for information on usage and redistribution * of this file. */ @@ -46,6 +50,11 @@ #include "itypes.h" #include "methodCache.h" #include "support.h" +#include "xprofiler.h" +#include "feedback.h" +#include "debugFile.h" +#include "fileSections.h" +#include "mangle.h" char* engine_name = "Just-in-time v3"; char* engine_version = KAFFEVERSION; @@ -158,6 +167,10 @@ goto done3; } +#if defined(KAFFE_FEEDBACK) + if( kaffe_feedback_file ) + lockMutex(kaffe_feedback_file); +#endif /* Only one in the translator at once. Must check the translation * hasn't been done by someone else once we get it. */ @@ -301,8 +314,12 @@ switch (base[pc]) { default: printf("Unknown bytecode %d\n", base[pc]); - unlockMutex(xmeth->class->centry); leaveTranslator(); +#if defined(KAFFE_FEEDBACK) + if( kaffe_feedback_file ) + unlockMutex(kaffe_feedback_file); +#endif + unlockMutex(xmeth->class->centry); postException(einfo, JAVA_LANG(VerifyError)); success = false; break; @@ -369,6 +386,10 @@ } leaveTranslator(); +#if defined(KAFFE_FEEDBACK) + if( kaffe_feedback_file ) + unlockMutex(kaffe_feedback_file); +#endif done3:; unlockMutex(xmeth->class->centry); @@ -449,7 +470,13 @@ uint32 i; bool res; jexceptionEntry* e; - +#if defined(KAFFE_XPROFILER) || defined(KAFFE_XDEBUGGING) + struct mangled_method *mm = 0; +#endif +#if defined(KAFFE_FEEDBACK) + char *sym = 0; +#endif + /* Work out new estimate of code per bytecode */ code_generated += code->memlen; bytecode_processed += METHOD_BYTECODE_LEN(meth); @@ -466,6 +493,44 @@ meth->c.ncode.ncode_start = code->mem; meth->c.ncode.ncode_end = (void*)((uintp)code->code + code->codelen); +#if defined(KAFFE_FEEDBACK) + if( kaffe_feedback_file && !meth->class->loader ) + { + sym = KMALLOC(strlen(CLASS_CNAME(meth->class)) + + 1 + /* '/' */ + strlen(meth->name->data) + + strlen(METHOD_SIGD(meth)) + + 1); + sprintf(sym, + "%s/%s%s", + CLASS_CNAME(meth->class), + meth->name->data, + METHOD_SIGD(meth)); + feedbackJITMethod(sym, code->code, code->codelen, true); + KFREE(sym); + } +#endif +#if defined(KAFFE_XPROFILER) || defined(KAFFE_XDEBUGGING) + if( ( +#if defined(KAFFE_XPROFILER) + xProfFlag || +#else + 0 || +#endif +#if defined(KAFFE_XDEBUGGING) + machine_debug_file) && +#else + 0) && +#endif + (mm = createMangledMethod()) ) + { + mangleMethod(mm, meth); + } +#endif +#if defined(KAFFE_XPROFILER) + profileFunction(mm, code->code, code->codelen); +#endif + /* Flush code out of cache */ #if defined(FLUSH_DCACHE) FLUSH_DCACHE(code->code, (void*)((uintp)code->code + code->codelen)); @@ -483,10 +548,74 @@ /* Translate line numbers table */ if (meth->lines != 0) { +#if defined(KAFFE_XDEBUGGING) + struct debug_file *df = machine_debug_file; + + if( df ) + { + /* Mark the start of this source file */ + addDebugInfo(df, + DIA_SourceFile, + meth->class->sourcefile, code->code, + DIA_Function, + meth, mm, meth->lines->entry[0].line_nr, + code->code, code->codelen, + DIA_DONE); + } +#endif for (i = 0; i < meth->lines->length; i++) { meth->lines->entry[i].start_pc = getInsnPC(meth->lines->entry[i].start_pc) + (uintp)code->code; +#if defined(KAFFE_XDEBUGGING) + if( df ) + { + /* Add line debugging */ + addDebugInfo(df, + DIA_SourceLine, + meth->lines->entry[i].line_nr, + meth->lines->entry[i].start_pc - + (uintp)code->code, + DIA_DONE); + } +#endif } +#if defined(KAFFE_XDEBUGGING) + if( df ) + { + /* + * Mark the end of the function. This needs to be here + * so that gdb doesn't get confused about the range of + * the function since that will be determined by the + * next debugging information that is added. + */ + addDebugInfo(df, + DIA_EndFunction, + code->code + code->codelen, + DIA_DONE); + } +#endif + } + else + { +#if defined(KAFFE_XDEBUGGING) + /* + * No line debugging, but we'd like a symbol to show up anyways + */ + if( machine_debug_file ) + { + addDebugInfo(machine_debug_file, + DIA_SourceFile, meth->class->sourcefile, + code->code, + DIA_Function, meth, mm, 0, + code->code, code->codelen, + DIA_EndFunction, + code->code + code->codelen, + DIA_DONE); + } +#endif } +#if defined(KAFFE_XPROFILER) || defined(KAFFE_XDEBUGGING) + deleteMangledMethod(mm); +#endif res = makeMethodActive(meth); assert(res == true); Index: kaffe/kaffe/kaffevm/jit3/registers.c diff -u kaffe/kaffe/kaffevm/jit3/registers.c:1.1.1.1 kaffe/kaffe/kaffevm/jit3/registers.c:1.2 --- kaffe/kaffe/kaffevm/jit3/registers.c:1.1.1.1 Thu May 4 16:13:09 2000 +++ kaffe/kaffe/kaffevm/jit3/registers.c Thu May 4 17:01:06 2000 @@ -226,7 +226,6 @@ regi = ®info[creg]; if ((regi->type & (Rglobal|Reserved)) == 0 && (regi->type & type) == type - && (regi->type & type) == type && regi->used < used) { used = regi->used; reg = creg; Index: kaffe/kaffe/kaffevm/systems/unix-jthreads/Makefile.am diff -u kaffe/kaffe/kaffevm/systems/unix-jthreads/Makefile.am:1.1.1.1 kaffe/kaffe/kaffevm/systems/unix-jthreads/Makefile.am:1.2 --- kaffe/kaffe/kaffevm/systems/unix-jthreads/Makefile.am:1.1.1.1 Thu May 4 16:13:11 2000 +++ kaffe/kaffe/kaffevm/systems/unix-jthreads/Makefile.am Thu May 4 17:01:19 2000 @@ -18,6 +18,7 @@ INCLUDES = \ -I. -I$(srcdir) -I../.. -I$(srcdir)/../.. \ -I$(top_builddir)/config -I$(top_srcdir)/config \ - -I$(top_builddir)/include -I$(top_srcdir)/include + -I$(top_builddir)/include -I$(top_srcdir)/include \ + -I$(top_srcdir)/kaffe/xprof DEFS = $(ENGINE_DEFS) -DKVER=\"$(KVER)\" Index: kaffe/kaffe/kaffevm/systems/unix-jthreads/jthread.c diff -u kaffe/kaffe/kaffevm/systems/unix-jthreads/jthread.c:1.1.1.1 kaffe/kaffe/kaffevm/systems/unix-jthreads/jthread.c:1.2 --- kaffe/kaffe/kaffevm/systems/unix-jthreads/jthread.c:1.1.1.1 Thu May 4 16:13:11 2000 +++ kaffe/kaffe/kaffevm/systems/unix-jthreads/jthread.c Mon May 22 09:39:50 2000 @@ -16,6 +16,7 @@ #include "jthread.h" #include "jsignal.h" +#include "xprofiler.h" /* Flags used for threading I/O calls */ @@ -122,12 +123,32 @@ jthread* currentJThread; +/* The arguments to a signal handler */ +#ifndef SIGNAL_ARGS +#define SIGNAL_ARGS(sig, sc) int sig +#endif + +/* Get a signal context pointer from signal arguments */ +#ifndef GET_SIGNAL_CONTEXT_POINTER +#define GET_SIGNAL_CONTEXT_POINTER(x) 0 +#endif + +/* A signal context pointer type, used in parameter lists/declarations */ +#ifndef SIGNAL_CONTEXT_POINTER +#define SIGNAL_CONTEXT_POINTER(x) void *##x +#endif + +/* Get the PC from a signal context pointer */ +#ifndef SIGNAL_PC +#define SIGNAL_PC(scp) 0 +#endif + /* * Function declarations. * Again, keep these static to ensure encapsulation. */ -static void handleInterrupt(int sig); -static void interrupt(int sig); +static void handleInterrupt(int sig, SIGNAL_CONTEXT_POINTER(sc)); +static void interrupt(SIGNAL_ARGS(sig, sc)); static void childDeath(void); static void handleIO(int); static void killThread(jthread *jtid); @@ -292,7 +313,7 @@ for (i = 1; i < NSIG; i++) { if (pendingSig[i]) { pendingSig[i] = 0; - handleInterrupt(i); + handleInterrupt(i, 0); } } sigPending = 0; @@ -357,7 +378,7 @@ * be running with all signals blocked. */ static void -interrupt(int sig) +interrupt(SIGNAL_ARGS(sig, sc)) { /* * If ints are blocked, this might indicate an inconsistent state of @@ -375,7 +396,21 @@ char c; pendingSig[sig] = 1; sigPending = 1; + +#if defined(KAFFE_XPROFILER) /* + * Since the regular handler won't run with the sig context we + * need to do the hit here + */ + if( sig == SIGVTALRM ) + { + SIGNAL_CONTEXT_POINTER(scp) = + GET_SIGNAL_CONTEXT_POINTER(sc); + + profileHit((char *)SIGNAL_PC(scp)); + } +#endif + /* * There is a race condition in handleIO() between * zeroing blockints and going into select(). * sigPipe+wouldlosewakeup is the hack that avoids @@ -443,7 +478,7 @@ /* * Handle the signal. */ - handleInterrupt(sig); + handleInterrupt(sig, GET_SIGNAL_CONTEXT_POINTER(sc)); /* * Leave the critical section. This may or may not cause a @@ -461,10 +496,14 @@ * priority. */ static void -handleVtAlarm(void) +handleVtAlarm(int sig, SIGNAL_CONTEXT_POINTER(sc)) { static int c; +#if defined(KAFFE_XPROFILER) + if( sc ) + profileHit((char *)SIGNAL_PC(sc)); +#endif if (preemptive) { internalYield(); } @@ -593,7 +632,7 @@ * result of intsRestore. */ static void -handleInterrupt(int sig) +handleInterrupt(int sig, SIGNAL_CONTEXT_POINTER(sc)) { switch(sig) { case SIGALRM: @@ -606,7 +645,7 @@ #if defined(SIGVTALRM) case SIGVTALRM: - handleVtAlarm(); + handleVtAlarm(sig, sc); break; #endif @@ -1044,6 +1083,13 @@ talive++; currentJThread = jtid; resumeThread(jtid); +#if defined(KAFFE_XPROFILER) + /* + * The profiler is started at program startup, we take over from here + * on out so we disable whatever one was installed + */ + disableProfileTimer(); +#endif /* Because of the handleVtAlarm hack (poll every 20 SIGVTALRMs) * we turn on the delivery of SIGVTALRM even if no actual time * slicing is possible because only one Java thread is running. Index: kaffe/kaffe/scripts/Makefile.am diff -u kaffe/kaffe/scripts/Makefile.am:1.1.1.1 kaffe/kaffe/scripts/Makefile.am:1.2 --- kaffe/kaffe/scripts/Makefile.am:1.1.1.1 Thu May 4 16:13:12 2000 +++ kaffe/kaffe/scripts/Makefile.am Mon May 22 09:40:12 2000 @@ -22,7 +22,7 @@ SCRIPTFILES_COMPAT=appletviewer javac javadoc endif -PSCRIPTFILES= install-jar kaffe +PSCRIPTFILES= install-jar kaffe kaffexprof nm2as.awk bin_SCRIPTS = $(SCRIPTFILES_KJC) $(SCRIPTFILES_SUN) \ $(SCRIPTFILES_COMPAT) $(PSCRIPTFILES) Index: kaffe/kaffe/scripts/kaffexprof.in diff -u /dev/null kaffe/kaffe/scripts/kaffexprof.in:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/scripts/kaffexprof.in Thu May 4 17:01:29 2000 @@ -0,0 +1,19 @@ +#! /bin/sh + +# Driver script for running gprof over kaffe generated gmon.out and symbol file + +AS="${AS-as}" +LD="${LD-ld}" +GPROF="${GPROF-gprof}" +AWK="${AWK-awk}" +NM="${NM-nm}" + +prefix="@prefix@" +exec_prefix="@exec_prefix@" + +if test -f "$1.s"; then + $NM -n @libexecdir@/@Kaffe_TRANSF@ | $AWK -f @bindir@/nm2as.awk | cat - $1.s | $AS - -o $1 + $GPROF $1 $2 +else + echo "Can't find assembler file $1.s" +fi Index: kaffe/kaffe/scripts/nm2as.awk diff -u /dev/null kaffe/kaffe/scripts/nm2as.awk:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/scripts/nm2as.awk Thu May 4 17:01:41 2000 @@ -0,0 +1,14 @@ + +BEGIN { + printf("/* Automatically generated by nm2as.awk */\n"); +} + +/[0-9]/ { + if( $1 != "U" ) + { + printf(".weak %s\n", $3); + printf("%s = 0x%s\n", $3, $1); + if( $3 == "_etext" ) + exit 0; + } +} Index: kaffe/kaffe/xprof/Makefile.am diff -u /dev/null kaffe/kaffe/xprof/Makefile.am:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/Makefile.am Thu May 4 17:07:53 2000 @@ -0,0 +1,48 @@ +# Makefile.in for xprof - A library to support cross language profiling +# +# Copyright (c) 2000 University of Utah and the Flux Group. +# All rights reserved. +# +# This file is licensed under the terms of the GNU Public License. +# See the file "license.terms" for information on usage and redistribution +# of this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# Contributed by the Flux Research Group, Department of Computer Science, +# University of Utah, http://www.cs.utah.edu/flux/ + +@engine_frag@ + +INCLUDES = -I$(top_srcdir)/kaffe/kaffevm/$(THREAD_DIR) $(ENGINE_INCLUDES) -I$(top_srcdir)/libltdl $(ENGINE_DEFS) -I$(top_srcdir)/kaffe/kaffevm + +lib_LTLIBRARIES = libkaffexprof.la + +libkaffexprof_la_LDFLAGS = -export-dynamic -release $(KVER) +libkaffexprof_la_DEPENDENCIES = \ + $(LIBLTDL) +libkaffexprof_la_LIBADD = $(libkaffexprof_la_DEPENDENCIES) $(VM_LIBS) $(M_LIBS) + +libkaffexprof_la_SOURCES = \ + callGraph.c \ + debugFile.c \ + feedback.c \ + fileSections.c \ + gmonFile.c \ + mangle.c \ + memorySamples.c \ + sectionFile.c \ + xprofiler.c + +noinst_HEADERS = \ + callGraph.h \ + debugFile.h \ + feedback.h \ + fileSections.h \ + gmon_out.h \ + gmonFile.h \ + mangle.h \ + memorySamples.h \ + sectionFile.h \ + xprofiler.h + +CONF_CFLAGS = @CFLAGS@ +CFLAGS = $(filter-out -pg,$(CONF_CFLAGS)) Index: kaffe/kaffe/xprof/callGraph.c diff -u /dev/null kaffe/kaffe/xprof/callGraph.c:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/callGraph.c Thu May 4 17:07:55 2000 @@ -0,0 +1,190 @@ +/* + * callGraph.c + * Routines for tracking the call graph of jitted code + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#if defined(KAFFE_XPROFILER) + +#include +#include +#include +#include + +#include "callGraph.h" +#include "jthread.h" + +/* + * Hash function for an arc, obviously we need something that will spread them + * out quite a bit. + */ +#define HASH_ARC(frompc, selfpc) \ + ((((unsigned long)(frompc)) + ((unsigned long)(selfpc))) \ + % CALL_GRAPH_TABLE_SIZE) + +struct call_graph *createCallGraph(int count) +{ + struct call_graph *retval; + struct call_arc *arcs; + + /* + * Allocate the arcs and the root structure, this needs to be executed + * early on since we use malloc. + */ + if( (retval = (struct call_graph *) + malloc(sizeof(struct call_graph))) && + (arcs = (struct call_arc *) + malloc(sizeof(struct call_arc) * count)) ) + { + int lpc; + + /* Initialize everything */ + retval->cg_misses = 0; + retval->cg_free = 0; + retval->cg_count = count; + retval->cg_arcs = arcs; + for( lpc = 0; lpc < CALL_GRAPH_TABLE_SIZE; lpc++ ) + { + retval->cg_table[lpc] = 0; + } + } + else + { + free(retval); + retval = 0; + } + return( retval ); +} + +void deleteCallGraph(struct call_graph *cg) +{ + if( cg ) + { + free(cg->cg_arcs); + free(cg); + } +} + +void resetCallGraph(struct call_graph *cg) +{ + int lpc; + + cg->cg_free = 0; + cg->cg_misses = 0; + /* Unlink the arcs */ + for( lpc = 0; lpc < CALL_GRAPH_TABLE_SIZE; lpc++ ) + cg->cg_table[lpc] = 0; +} + +extern int blockInts; + +void arcHit(struct call_graph *cg, char *frompc, char *selfpc) +{ + struct call_arc *ca; + int index; + + /* Get the index in the hash table for this arc */ + index = HASH_ARC(frompc, selfpc); + ca = cg->cg_table[index]; + /* Walk over the arcs in the table searching for a match */ + while( ca ) + { + if( (ca->ca_from == frompc) && (ca->ca_to == selfpc) ) + { + /* Found a match, increase the count */ + ca->ca_count++; + break; + } + ca = ca->ca_next; + } + if( !ca ) + { + /* + * No call_arc struct was found so this is the first time we've + * seen this arc, get a new one and add it in. + */ + if( cg->cg_free < cg->cg_count ) + { + ca = &cg->cg_arcs[cg->cg_free++]; + ca->ca_next = cg->cg_table[index]; + cg->cg_table[index] = ca; + ca->ca_from = frompc; + ca->ca_to = selfpc; + ca->ca_count = 1; + } + else + { + /* No more free call_arcs, record the miss */ + cg->cg_misses++; + } + } +} + +int writeCallGraph(struct call_graph *cg, struct gmon_file *gf) +{ +#if defined(KAFFE_CPROFILER) + struct gmonparam *gp = &_gmonparam; + int from_len; +#endif + int retval = 1, lpc; + + /* Walk over all of the arcs and output any that are non zero */ + for( lpc = 0; (lpc < cg->cg_count) && retval; lpc++ ) + { + if( cg->cg_arcs[lpc].ca_count ) + { + struct call_arc *ca = &cg->cg_arcs[lpc]; + + retval = writeGmonRecord(gf, + GRA_Type, GMON_TAG_CG_ARC, + GRA_FromPC, ca->ca_from, + GRA_SelfPC, ca->ca_to, + GRA_Count, ca->ca_count, + GRA_DONE); + } + } + +#if defined(KAFFE_CPROFILER) + /* + * Here we write out any arcs that were generated by the C compiler's + * mcount. + */ + from_len = gp->fromssize / sizeof(*gp->froms); + for( lpc = 0; (lpc < from_len) && retval; lpc++ ) + { + if( gp->froms[lpc] ) + { + char *frompc; + int to_index; + + frompc = (char *)(gp->lowpc + + (lpc * gp->hashfraction * + sizeof(*gp->froms))); + for( to_index = gp->froms[lpc]; + to_index != 0; + to_index = gp->tos[to_index].link ) + { + retval = writeGmonRecord( + gf, + GRA_Type, GMON_TAG_CG_ARC, + GRA_FromPC, frompc, + GRA_SelfPC, gp->tos[to_index].selfpc, + GRA_Count, gp->tos[to_index].count, + GRA_DONE); + } + } + } +#endif + return( retval ); +} + +#endif Index: kaffe/kaffe/xprof/callGraph.h diff -u /dev/null kaffe/kaffe/xprof/callGraph.h:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/callGraph.h Thu May 4 17:07:57 2000 @@ -0,0 +1,62 @@ +/* + * callGraph.h + * Routines for tracking the call graph of jitted code + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#ifndef __callgraph_h +#define __callgraph_h + +#include "gmonFile.h" + +/* A call arc as stored in a hash table */ +struct call_arc { + struct call_arc *ca_next; /* Hash table link */ + char *ca_from; /* Caller */ + char *ca_to; /* Callee */ + int ca_count; /* Traversal count */ +}; + +/* Size of the hash table */ +#define CALL_GRAPH_TABLE_SIZE 2047 + +/* The root data structure for a collection of call graphs */ +struct call_graph { + int cg_misses; /* Arcs we couldn't record */ + int cg_free; /* Index of the next free call_arc */ + int cg_count; /* Number of call_arc's allocated */ + struct call_arc *cg_table[CALL_GRAPH_TABLE_SIZE]; /* Hashtable */ + struct call_arc *cg_arcs; /* Array of call_arc structures */ +}; + +/* + * Create a call graph and allocate `count' arcs for it + */ +struct call_graph *createCallGraph(int count); +/* + * Delete the call graph object + */ +void deleteCallGraph(struct call_graph *cg); +/* + * Increment the counter for this call arc + */ +void arcHit(struct call_graph *cg, char *frompc, char *selfpc); +/* + * Reset the values in the call graph + */ +void resetCallGraph(struct call_graph *cg); +/* + * Write the call graph to the given gmon file + */ +int writeCallGraph(struct call_graph *cg, struct gmon_file *gf); + +#endif /* __callgraph_h */ Index: kaffe/kaffe/xprof/debugFile.c diff -u /dev/null kaffe/kaffe/xprof/debugFile.c:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/debugFile.c Thu May 4 17:07:58 2000 @@ -0,0 +1,237 @@ +/* + * debugFile.c + * Routines for generating an assembly file with debugging information + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#if defined(KAFFE_XDEBUGGING) || defined(KAFFE_XPROFILER) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jmalloc.h" +#include "classMethod.h" +#include "code.h" + +#include "mangle.h" +#include "debugFile.h" +#include "xprofiler.h" + +struct debug_file *machine_debug_file; +char *machine_debug_filename = 0; + +static char *debug_header = "This file was automatically generated by Kaffe"; + +struct debug_file *createDebugFile(char *filename) +{ + struct debug_file *retval = 0; + + /* Allocate space for the debug_file struct and filename */ + if( (retval = (struct debug_file *) + KMALLOC(sizeof(struct debug_file) + strlen(filename) + 1)) ) + { + retval->df_filename = (char *)(retval + 1); + strcpy(retval->df_filename, filename); + if( (retval->df_file = fopen(retval->df_filename, "w")) ) + { + addDebugInfo(retval, + DIA_Comment, debug_header, + DIA_DONE); + } + else + { + KFREE(retval); + retval = 0; + } + } + return( retval ); +} + +void deleteDebugFile(struct debug_file *df) +{ + if( df ) + { + /* If there was an error in writing the file remove it */ + if( ferror(df->df_file) ) + remove(df->df_filename); + fclose(df->df_file); + KFREE(df); + } +} + +int addDebugInfo(struct debug_file *df, int tag, ...) +{ + int retval = 1; + va_list args; + +#if defined(KAFFE_XPROFILER) + xProfilingOff(); +#endif + va_start(args, tag); + if( df ) + { + /* Walk over the arguments until we hit the terminator */ + while( tag != DIA_DONE ) + { + struct mangled_method *mm; + char *str, *name, *addr; + int line, size; + Method *meth; + + switch( tag ) + { + case DIA_FunctionSymbolS: + name = va_arg(args, char *); + addr = va_arg(args, char *); + size = va_arg(args, int); + fprintf(df->df_file, + ".weak %s\n" + "%s = %p\n", + name, + name, + addr); + if( size > 0 ) + { + fprintf(df->df_file, + ".weak %s_end\n" + "%s_end = %p\n", + name, + name, + addr + size); + } + break; + case DIA_FunctionSymbol: + mm = va_arg(args, struct mangled_method *); + addr = va_arg(args, char *); + size = va_arg(args, int); + fprintf(df->df_file, ".weak "); + printMangledMethod(mm, df->df_file); + fprintf(df->df_file, "\n"); + printMangledMethod(mm, df->df_file); + fprintf(df->df_file, " = %p\n", addr); + if( size > 0 ) + { + fprintf(df->df_file, ".weak "); + printMangledMethod(mm, df->df_file); + fprintf(df->df_file, "_end\n"); + printMangledMethod(mm, df->df_file); + fprintf(df->df_file, + "_end = %p\n", + addr + size); + } + break; + case DIA_Function: + meth = va_arg(args, Method *); + mm = va_arg(args, struct mangled_method *); + line = va_arg(args, int); + addr = va_arg(args, char *); + size = va_arg(args, int); + /* Add the stabs info to the file */ + fprintf(df->df_file, + " /* START %s/%s%s */\n" + ".stabs \"", + meth->name->data, + CLASS_CNAME(meth->class), + METHOD_SIGD(meth)); + printMangledMethod(mm, df->df_file); + fprintf(df->df_file, ":F\",%d,0,%d,%p\n", + N_FUN, + line, + addr); + /* Add the symbols to the file */ + fprintf(df->df_file, + " /* Symbol: %s/%s%s */\n" + ".weak ", + meth->name->data, + CLASS_CNAME(meth->class), + METHOD_SIGD(meth)); + printMangledMethod(mm, df->df_file); + fprintf(df->df_file, "\n"); + printMangledMethod(mm, df->df_file); + fprintf(df->df_file, + " = %p\n" + ".weak ", + addr); + printMangledMethod(mm, df->df_file); + fprintf(df->df_file, "_end\n"); + printMangledMethod(mm, df->df_file); + fprintf(df->df_file, + "_end = %p\n", + addr + size); + break; + case DIA_Symbol: + name = va_arg(args, char *); + addr = va_arg(args, char *); + fprintf(df->df_file, "%s = %p\n", name, addr); + break; + case DIA_EndFunction: + addr = va_arg(args, char *); + /* + * Add an empty filename name symbol so that + * the debugger will know where the real + * end of the information is. Otherwise, it + * uses the next symbol which may not be + * contiguous. + */ + fprintf(df->df_file, + ".stabs \"\",%d,0,0,%p\n", + N_SO, + addr); + break; + case DIA_SourceLine: + line = va_arg(args, int); + addr = va_arg(args, char *); + fprintf(df->df_file, + ".stabn %d,0,%d,%p\n", + N_SLINE, + line, + addr); + break; + case DIA_SourceFile: + name = va_arg(args, char *); + addr = va_arg(args, char *); + fprintf(df->df_file, + "\n\n.stabs \"%s\",%d,0,0,%p\n", + name, + N_SO, + addr); + break; + case DIA_Comment: + str = va_arg(args, char *); + fprintf(df->df_file, + "/* %s */\n", + str); + break; + } + /* Get the next tag to process */ + tag = va_arg(args, int); + } + fflush(df->df_file); + /* Check for I/O error */ + if( ferror(df->df_file) ) + retval = 0; + } + va_end(args); +#if defined(KAFFE_XPROFILER) + xProfilingOn(); +#endif + return( retval ); +} + +#endif /* KAFFE_XDEBUGGING */ Index: kaffe/kaffe/xprof/debugFile.h diff -u /dev/null kaffe/kaffe/xprof/debugFile.h:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/debugFile.h Thu May 4 17:08:00 2000 @@ -0,0 +1,85 @@ +/* + * debugFile.h + * Routines for generating an assembly file with debugging information + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#ifndef __debugfile_h +#define __debugfile_h + +#include + +/* The root structure for a debugging information file */ +struct debug_file { + char *df_filename; + FILE *df_file; +}; + +/* Argument tags for the varargs addDebugInfo */ +enum { + DIA_DONE, /* Terminate the tag list */ + DIA_Comment, /* (char *name) Add a comment to the file */ + DIA_FunctionSymbol, /* + * (struct mangled_method *mm, + * char *addr, int size) Add a mangled + * symbol to the file. If size > 0 then + * then another symbol is added with the same + * name and `_end' appeneded to it. + */ + DIA_FunctionSymbolS, /* + * (char *name, char *addr, int size) Add a + * symbol to the file. If size > 0 then then + * another symbol is added with the same name + * and `_end' appeneded to it. + */ + DIA_Symbol, /* + * (char *name, char *addr) Add a generic + * symbol to the file. + */ + DIA_Function, /* + * (Method *, struct mangled_method *mm, + * int line, char *addr, int size) + * Add a information about a function + */ + DIA_EndFunction, /* (char *addr) Mark the end of a function */ + DIA_SourceLine, /* (int line, char *addr) Add a source line */ + DIA_SourceFile, /* + * (char *name, char *addr) Add information + * about a source file + */ +}; + +/* + * Create the root structure and corresponding output file. + */ +struct debug_file *createDebugFile(char *filename); +/* + * Delete the root structure and close the output file. + */ +void deleteDebugFile(struct debug_file *df); +/* + * Add debugging information to the file, the function takes a list of + * arguments that indicate the type of information to add. For example, + * to add information about the `debugFile.c' file, you would call the function + * like so: + * + * addDebugInfo(df, + * DIA_SourceFile, "debugFile.c", debugFileStartAddress, + * DIA_DONE); + */ +int addDebugInfo(struct debug_file *file, int tag, ...); + +/* Default debugging file for the virtual machine */ +extern struct debug_file *machine_debug_file; +extern char *machine_debug_filename; + +#endif /* __debugfile_h */ Index: kaffe/kaffe/xprof/feedback.c diff -u /dev/null kaffe/kaffe/xprof/feedback.c:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/feedback.c Thu May 4 17:08:01 2000 @@ -0,0 +1,298 @@ +/* + * feedback.c + * Routines for generating information that can be fed back into kaffe for + * future runs. + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#include +#include +#include + +#include "gtypes.h" +#include "access.h" +#include "classMethod.h" +#include "lookup.h" +#include "support.h" +#include "stringSupport.h" +#include "methodCache.h" +#include "thread.h" +#include "external.h" + +#include "feedback.h" +#include "fileSections.h" + +/* XXX Temporary for now until we define interface header file that + * declares "translate" + */ +#if defined(TRANSLATOR) +#if defined(JIT3) +#include "jit3/machine.h" +#else +#include "jit/machine.h" +#endif +#endif + +#define FEEDBACKSTACKSIZE 4096 + +struct section_file *kaffe_feedback_file = 0; +char *feedback_filename = 0; + +int feedbackFile(char *filename) +{ + int retval = 0; + + /* Create a section file object to store the feedback information */ + if( (kaffe_feedback_file = createSectionFile()) ) + { + setSectionFileName(kaffe_feedback_file, filename); + /* + * Add an atexit call to syncFeedback so we know the + * information got out + */ + if( !atexit((void (*)(void))syncFeedback) ) + retval = 1; + else + { + deleteSectionFile(kaffe_feedback_file); + kaffe_feedback_file = 0; + } + } + return( retval ); +} + +int syncFeedback(void) +{ + int retval = 0; + + if( kaffe_feedback_file && syncSectionFile(kaffe_feedback_file) ) + { + retval = 1; + } + return( retval ); +} + +/* + * Function that is called by the sections walker + */ +static int feedbackWalker(void *arg, + struct section_file *sf, + struct section_file_data *sfd) +{ + int retval = 1; + + /* Check for library section */ + if( sfd->sfd_type == &lib_section ) + { + struct lib_section_data *lsd = (struct lib_section_data *)sfd; + + if( lsd->lsd_flags & LSDF_PRELOAD ) + loadNativeLibrary2(sfd->sfd_name, 0, 0, 0); + } + /* Check for jit-code section */ + else if( sfd->sfd_type == &jit_section ) + { +#if defined(TRANSLATOR) + struct jit_section_data *jsd = (struct jit_section_data *)sfd; + + if( jsd->jsd_flags & JSDF_PRECOMPILE ) + { + int len, lpc, sig_start = -1, meth_start = -1; + Utf8Const *u8cname, *u8mname, *u8sig; + Hjava_lang_Class *cls; + char *full_name; + + /* + * Parse the name of the section to get the class, + * method, and signature + */ + full_name = sfd->sfd_name; + len = strlen(full_name); + for( lpc = len - 1; + (lpc >= 0) && (meth_start < 0); + lpc-- ) + { + switch( full_name[lpc] ) + { + case '(': + sig_start = lpc; + break; + case '/': + if( sig_start > 0 ) + meth_start = lpc + 1; + break; + } + } + if( (sig_start > 0) && (meth_start > 0) ) + { + jobject loader = 0; + errorInfo info; + + /* Get the right strings and find the class */ + u8cname = utf8ConstNew(full_name, + meth_start - 1); + u8mname = utf8ConstNew(&full_name[meth_start], + sig_start - meth_start); + u8sig = utf8ConstNew(&full_name[sig_start], + len - sig_start); + if( u8cname && u8mname && u8sig && + (cls = loadClass(u8cname, loader, &info)) ) + { + Method *meth; + + if( (meth = findMethodLocal(cls, + u8mname, + u8sig)) && + !(meth->accflags & ACC_NATIVE) ) + { + if( translate(meth, &info) ) + { + } + else + { + fprintf(stderr, + "Feedback: " + " Precompile " + "failed for " + "%s!\n", + full_name); + } + } + else if( !meth ) + { + fprintf(stderr, + "Feedback: Didn't " + "find method" + " %s\n", + full_name); + } + } + else + { + fprintf(stderr, + "Feedback: Couldn't load " + "class %s\n", + u8cname->data); + } + utf8ConstRelease(u8cname); + utf8ConstRelease(u8mname); + utf8ConstRelease(u8sig); + } + else + { + fprintf(stderr, + "Feedback: Malformed method `%s'\n", + full_name); + } + } +#else + { + static int precompile_msg = 0; + + if( !precompile_msg ) + { + precompile_msg = 1; + fprintf(stderr, + "Feedback: Cannot precompile java for " + "the interpreter\n"); + } + } +#endif + } + return( retval ); +} + +static void feedbackRunnable(void *arg) +{ + /* Walk over the sections with our function */ + walkFileSections(kaffe_feedback_file, feedbackWalker, 0); +} + +int processFeedback(void) +{ + int retval = 1; + + if( kaffe_feedback_file ) + { + feedbackRunnable(0); + retval = 1; + } + return( retval ); +} + +int feedbackJITMethod(char *method, char *address, int size, int precompile) +{ + struct section_file_data *sfd; + struct jit_section_data *jsd; + int retval = 0, iLockRoot; + + if( !kaffe_feedback_file ) + return( 0 ); + lockMutex(kaffe_feedback_file); + if( !(sfd = findSectionInFile(kaffe_feedback_file, + &jit_section, method)) ) + { + /* + * If the section doesn't exist we need to create and add it. + * We only set precompile here since the user might've changed + * the file to specify otherwise. + */ + if( (sfd = createFileSection(jit_section.fs_name, method, + "precompile", precompile ? + "true" : "false", + NULL)) ) + { + addSectionToFile(kaffe_feedback_file, sfd); + } + } + if( sfd ) + { + /* Set whatever attributes are interesting */ + jsd = (struct jit_section_data *)sfd; + jsd->jsd_address = address; + jsd->jsd_size = size; + retval = 1; + } + unlockMutex(kaffe_feedback_file); + return( retval ); +} + +int feedbackLibrary(char *name, int preload) +{ + struct section_file_data *sfd; + int retval = 0, iLockRoot; + + if( !kaffe_feedback_file ) + return( 0 ); + lockMutex(kaffe_feedback_file); + if( !(sfd = findSectionInFile(kaffe_feedback_file, + &lib_section, name)) ) + { + /* + * If the section doesn't exist we need to create and add it. + * We only set preload here since the user might've changed + * the file to specify otherwise. + */ + if( (sfd = createFileSection(lib_section.fs_name, name, + "preload", preload ? + "true" : "false", + NULL)) ) + { + addSectionToFile(kaffe_feedback_file, sfd); + retval = 1; + } + } + else + retval = 1; + unlockMutex(kaffe_feedback_file); + return( retval ); +} Index: kaffe/kaffe/xprof/feedback.h diff -u /dev/null kaffe/kaffe/xprof/feedback.h:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/feedback.h Thu May 4 17:08:02 2000 @@ -0,0 +1,48 @@ +/* + * feedback.h + * Routines for generating information that can be fed back into kaffe for + * future runs. + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#ifndef __feedback_h +#define __feedback_h + +#include "sectionFile.h" + +/* + * Specify the file to read/store any information + */ +int feedbackFile(char *filename); +/* + * Process information read from the feedback file + */ +int processFeedback(void); +/* + * Synchronize the feedback file, reading in anything new, or writing out + * new information. + */ +int syncFeedback(void); +/* + * Add information about a jitted method + */ +int feedbackJITMethod(char *method, char *address, int size, int precompile); +/* + * Add information about a dynamically loaded library + */ +int feedbackLibrary(char *name, int preload); + +/* This is the section file that holds all the feedback information. */ +extern struct section_file *kaffe_feedback_file; +extern char *feedback_filename; + +#endif /* __feedback_h */ Index: kaffe/kaffe/xprof/fileSections.c diff -u /dev/null kaffe/kaffe/xprof/fileSections.c:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/fileSections.c Thu May 4 17:08:04 2000 @@ -0,0 +1,393 @@ +/* + * fileSections.c + * Definitions for feedback file sections + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#include +#include +#include +#include +#include + +#include "jmalloc.h" + +#include "sectionFile.h" +#include "fileSections.h" + +/* Set a value in the jit section structure by name */ +static int setJITSectionValue(struct jit_section_data *jsd, + char *tag, char *value) +{ + int retval = 1; + + if( !strcmp(tag, "precompile") ) + { + jsd->jsd_flags = parseFlagString(value, + jsd->jsd_flags, + JSDF_PRECOMPILE); + } + else if( !strcmp(tag, "address") ) + { + void *addr; + + if( sscanf(value, "%p", &addr) != 1 ) + jsd->jsd_address = addr; + else + retval = 0; + } + else if( !strcmp(tag, "size") ) + { + if( sscanf(value, "%lu", &jsd->jsd_size) != 1 ) + retval = 0; + } + return( retval ); +} + +/* Write out the current value of a jit attribute */ +static int writeJITSectionValue(struct jit_section_data *jsd, + struct parse_state *ps, + char *tag, char *value, + FILE *out_file) +{ + char scratch[128]; + int retval = 1; + + /* + * Figure out which attribute we're processing and construct the new + * value + */ + if( !tag ) + { + tag = ""; + value = ""; + } + else if( !strcmp(tag, "precompile") ) + { + value = makeFlagString(jsd->jsd_flags, JSDF_PRECOMPILE, value); + } + else if( !strcmp(tag, "size") ) + { + sprintf(scratch, "%lu", jsd->jsd_size); + value = scratch; + } + else if( !strcmp(tag, "address") ) + { + sprintf(scratch, "%p", jsd->jsd_address); + value = scratch; + } + /* + * parseSectionLine will do the actual writing so its formatted + * correctly + */ + retval = parseSectionLine(ps, &tag, &value, out_file); + return( retval ); +} + +static int jitSectionHandler(struct file_section *fs, struct section_file *sf, + int method, ...) +{ + int retval = 1; + va_list args; + + va_start(args, method); + switch( method ) + { + case SFM_CREATE: + { + struct section_file_data **out_sfd; + struct jit_section_data *jsd; + char *name, *tag, *value; + va_list values; + + /* Get the args */ + out_sfd = va_arg(args, struct section_file_data **); + name = va_arg(args, char *); + values = va_arg(args, va_list); + /* Allocate the section struct and initialize it */ + if( (jsd = (struct jit_section_data *) + KMALLOC(sizeof(struct jit_section_data))) ) + { + assert(name[0]); + jsd->jsd_link.sfd_type = fs; + jsd->jsd_link.sfd_flags = 0; + jsd->jsd_link.sfd_name = name; + jsd->jsd_flags = 0; + jsd->jsd_size = 0; + jsd->jsd_address = 0; + /* Process the rest of the args */ + tag = va_arg(values, char *); + while( tag ) + { + value = va_arg(values, char *); + setJITSectionValue(jsd, tag, value); + tag = va_arg(values, char *); + } + *out_sfd = &jsd->jsd_link; + } + else + { + retval = 0; + } + } + break; + case SFM_DELETE: + { + struct section_file_data *sfd; + + sfd = va_arg(args, struct section_file_data *); + KFREE(sfd); + } + break; + case SFM_CACHE: + { + char *line, *tag = 0, *value = 0; + struct jit_section_data *jsd; + struct parse_state *ps; + int line_len; + + jsd = va_arg(args, struct jit_section_data *); + ps = va_arg(args, struct parse_state *); + line = va_arg(args, char *); + line_len = va_arg(args, int); + /* Read the line in and set the value */ + parseSectionLine(ps, &tag, &value, 0); + if( tag ) + setJITSectionValue(jsd, tag, value); + } + break; + case SFM_FLUSH: + { + struct jit_section_data *jsd; + char *line, *tag, *value; + struct parse_state *ps; + FILE *out_file; + int line_len; + + jsd = va_arg(args, struct jit_section_data *); + ps = va_arg(args, struct parse_state *); + line = va_arg(args, char *); + line_len = va_arg(args, int); + out_file = va_arg(args, FILE *); + if( line ) + { + /* + * Parse the line and write out the current + * value + */ + retval = parseSectionLine(ps, &tag, &value, 0); + retval = writeJITSectionValue(jsd, ps, + tag, value, + out_file); + } + else if( jsd->jsd_link.sfd_flags & SFDF_DIRTY ) + { + /* + * The section wasn't in the file before, write + * the whole thing out + */ + fprintf(out_file, + "\tprecompile %s\n", + jsd->jsd_flags & JSDF_PRECOMPILE ? + "true" : "false"); + fprintf(out_file, + "\tsize %lu\n", + jsd->jsd_size); + fprintf(out_file, + "\taddress %p\n", + jsd->jsd_address); + } + else + { + } + } + break; + default: + break; + } + return( retval ); +} + +struct file_section jit_section; + +/* Set a value in the lib section structure by name */ +static int setLibSectionValue(struct lib_section_data *lsd, + char *tag, char *value) +{ + int retval = 1; + + if( !strcmp(tag, "preload") ) + { + lsd->lsd_flags = parseFlagString(value, + lsd->lsd_flags, + LSDF_PRELOAD); + } + return( retval ); +} + +/* Write out the current value of a jit attribute */ +static int writeLibSectionValue(struct lib_section_data *lsd, + struct parse_state *ps, + char *tag, char *value, + FILE *out_file) +{ + int retval = 1; + + /* + * Figure out which attribute we're processing and construct the new + * value + */ + if( !tag ) + { + tag = ""; + value = ""; + } + else if( !strcmp(tag, "preload") ) + { + value = makeFlagString(lsd->lsd_flags, LSDF_PRELOAD, value); + } + /* + * parseSectionLine will do the actual writing so its formatted + * correctly + */ + retval = parseSectionLine(ps, &tag, &value, out_file); + return( retval ); +} + +static int libSectionHandler(struct file_section *fs, struct section_file *sf, + int method, ...) +{ + int retval = 1; + va_list args; + + va_start(args, method); + switch( method ) + { + case SFM_CREATE: + { + struct section_file_data **out_sfd; + struct lib_section_data *lsd; + char *name, *tag, *value; + va_list values; + + /* Get the args */ + out_sfd = va_arg(args, struct section_file_data **); + name = va_arg(args, char *); + values = va_arg(args, va_list); + /* Allocate the section struct and initialize it */ + if( (lsd = (struct lib_section_data *) + KMALLOC(sizeof(struct lib_section_data))) ) + { + assert(name[0]); + lsd->lsd_link.sfd_type = fs; + lsd->lsd_link.sfd_flags = 0; + lsd->lsd_link.sfd_name = name; + lsd->lsd_flags = 0; + /* Process the rest of the args */ + tag = va_arg(values, char *); + while( tag ) + { + value = va_arg(values, char *); + setLibSectionValue(lsd, tag, value); + tag = va_arg(values, char *); + } + *out_sfd = &lsd->lsd_link; + } + else + { + retval = 0; + } + } + break; + case SFM_DELETE: + { + struct section_file_data *sfd; + + sfd = va_arg(args, struct section_file_data *); + KFREE(sfd); + } + break; + case SFM_CACHE: + { + char *line, *tag = 0, *value = 0; + struct lib_section_data *lsd; + struct parse_state *ps; + int line_len; + + lsd = va_arg(args, struct lib_section_data *); + ps = va_arg(args, struct parse_state *); + line = va_arg(args, char *); + line_len = va_arg(args, int); + /* Read the line in and set the value */ + parseSectionLine(ps, &tag, &value, 0); + if( tag ) + setLibSectionValue(lsd, tag, value); + } + break; + case SFM_FLUSH: + { + struct lib_section_data *lsd; + char *line, *tag, *value; + struct parse_state *ps; + FILE *out_file; + int line_len; + + lsd = va_arg(args, struct lib_section_data *); + ps = va_arg(args, struct parse_state *); + line = va_arg(args, char *); + line_len = va_arg(args, int); + out_file = va_arg(args, FILE *); + if( line ) + { + /* + * Parse the line and write out the current + * value + */ + retval = parseSectionLine(ps, &tag, &value, 0); + retval = writeLibSectionValue(lsd, ps, + tag, value, + out_file); + } + else if( lsd->lsd_link.sfd_flags & SFDF_DIRTY ) + { + /* + * The section wasn't in the file before, write + * the whole thing out + */ + fprintf(out_file, + "\tpreload %s\n", + lsd->lsd_flags & LSDF_PRELOAD ? + "true" : "false"); + } + else + { + } + } + break; + default: + break; + } + return( retval ); +} + +struct file_section lib_section; + +void installFileSections(void) +{ + /* Setup and install the handlers */ + jit_section.fs_name = "jit-code"; + jit_section.fs_handler = jitSectionHandler; + addSectionType(&jit_section); + lib_section.fs_name = "library"; + lib_section.fs_handler = libSectionHandler; + addSectionType(&lib_section); +} Index: kaffe/kaffe/xprof/fileSections.h diff -u /dev/null kaffe/kaffe/xprof/fileSections.h:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/fileSections.h Thu May 4 17:08:10 2000 @@ -0,0 +1,57 @@ +/* + * fileSections.h + * Definitions for feedback file sections + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#ifndef __filesections_h +#define __filesections_h + +#include "sectionFile.h" + +enum { + JSDB_PRECOMPILE, /* Precompile the method */ +}; + +enum { + JSDF_PRECOMPILE = (1L << JSDB_PRECOMPILE), +}; + +/* Structure that stores information about jitted code */ +struct jit_section_data { + struct section_file_data jsd_link; + unsigned long jsd_flags; /* Mask of the above flags */ + unsigned long jsd_size; /* Size of the code */ + char *jsd_address; /* Address of the code */ +}; + +extern struct file_section jit_section; + +enum { + LSDB_PRELOAD, /* Preload the library */ +}; + +enum { + LSDF_PRELOAD = (1L << LSDB_PRELOAD), +}; + +/* Structure that stores information about dynamic libraries */ +struct lib_section_data { + struct section_file_data lsd_link; + unsigned long lsd_flags; /* Mask of the above flags */ +}; + +extern struct file_section lib_section; + +void installFileSections(void); + +#endif /* __filesections_h */ Index: kaffe/kaffe/xprof/gmonFile.c diff -u /dev/null kaffe/kaffe/xprof/gmonFile.c:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/gmonFile.c Thu May 4 17:08:13 2000 @@ -0,0 +1,301 @@ +/* + * gmonFile.c + * gmon_out file handling routines + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#if defined(KAFFE_XPROFILER) + +#include +#include +#include +#include +#include +#include + +#include "jmalloc.h" + +#include "gmonFile.h" + +#define min(x, y) ((x < y) ? x : y) + +struct gmon_file *createGmonFile(char *filename) +{ + struct gmon_file *retval = 0; + FILE *file; + + if( (file = fopen(filename, "w")) ) + { + struct gmon_hdr gh; + + /* Write out the header */ + memcpy(gh.cookie, GMON_MAGIC, sizeof(gh.cookie)); + *((int *)gh.version) = GMON_VERSION; + bzero(&gh.spare, sizeof(gh.spare)); + fwrite(&gh, sizeof(struct gmon_hdr), 1, file); + if( !ferror(file) && + (retval = (struct gmon_file *) + KMALLOC(sizeof(struct gmon_file))) ) + { + retval->gf_name = filename; + retval->gf_file = file; + retval->gf_low = 0; + retval->gf_addr = 0; + retval->gf_high = 0; + /* Start out writing records */ + retval->gf_state = GFS_RECORD; + } + else + { + fclose(file); + remove(filename); + } + } + return( retval ); +} + +void deleteGmonFile(struct gmon_file *gf) +{ + if( gf ) + { + fclose(gf->gf_file); + /* Delete the file if there was an error in writing */ + if( gf->gf_state == GFS_ERROR ) + remove(gf->gf_name); + KFREE(gf); + } +} + +long writeGmonRecord(struct gmon_file *gf, int tag, ...) +{ + struct gmon_cg_arc_record cga; + long retval = 0, rewrite = -1; + struct gmon_hist_hdr ghh; + unsigned char hdr_tag; + va_list args; + + va_start(args, tag); + /* Walk over the tag list getting whatever info we care about */ + while( tag != GRA_DONE ) + { + switch( tag ) + { + case GRA_Type: + hdr_tag = va_arg(args, int); + /* Initialize the record structure */ + switch(hdr_tag) + { + case GMON_TAG_TIME_HIST: + bzero(ghh.low_pc, sizeof(ghh.low_pc)); + bzero(ghh.high_pc, sizeof(ghh.high_pc)); + bzero(ghh.hist_size, sizeof(ghh.hist_size)); + *((int *)ghh.prof_rate) = 100; + strcpy(ghh.dimen, "seconds"); + ghh.dimen_abbrev = 's'; + break; + case GMON_TAG_CG_ARC: + bzero(cga.from_pc, sizeof(cga.from_pc)); + bzero(cga.self_pc, sizeof(cga.self_pc)); + bzero(cga.count, sizeof(cga.count)); + break; + } + break; + case GRA_Rewrite: + rewrite = va_arg(args, long); + break; + + /* Histogram attributes */ + case GRA_LowPC: + { + char *lowpc = va_arg(args, char *); + + *((char **)ghh.low_pc) = lowpc; + gf->gf_low = lowpc; + } + break; + case GRA_HighPC: + *((char **)ghh.high_pc) = va_arg(args, char *); + break; + case GRA_PCSize: + *((char **)ghh.high_pc) = *((char **)ghh.low_pc) + + va_arg(args, int); + break; + case GRA_Dimension: + strcpy(ghh.dimen, va_arg(args, char *)); + break; + case GRA_DimensionAbbrev: + ghh.dimen_abbrev = va_arg(args, char); + break; + case GRA_ProfilingRate: + *((int *)ghh.prof_rate) = va_arg(args, int); + break; + + /* Call graph attributes */ + case GRA_FromPC: + *((char **)cga.from_pc) = va_arg(args, char *); + break; + case GRA_SelfPC: + *((char **)cga.self_pc) = va_arg(args, char *); + break; + case GRA_Count: + *((int *)cga.count) = va_arg(args, int); + break; + + default: + break; + } + tag = va_arg(args, int); + } + + if( rewrite == -1 ) + { + assert(gf->gf_state == GFS_RECORD); + /* Write out the tag */ + fwrite(&hdr_tag, sizeof(hdr_tag), 1, gf->gf_file); + } + if( !ferror(gf->gf_file) ) + { + /* Write out the actual record */ + switch(hdr_tag) + { + case GMON_TAG_TIME_HIST: + *((int *)ghh.hist_size) = (*((char **)ghh.high_pc) - + *((char **)ghh.low_pc)) / + HISTFRACTION; + gf->gf_high = *((char **)ghh.high_pc); + retval = ftell(gf->gf_file); + /* If this is a rewrite, seek back to the record */ + if( rewrite != -1 ) + fseek(gf->gf_file, rewrite, SEEK_SET); + fwrite(&ghh, sizeof(ghh), 1, gf->gf_file); + if( !ferror(gf->gf_file) ) + { + gf->gf_state = GFS_SAMPLES; + /* + * If it was a rewrite, seek forward to where + * we were + */ + if( rewrite != -1 ) + { + /* + * If the high was changed we might + * need to switch states. + */ + if( gf->gf_addr == gf->gf_high ) + gf->gf_state = GFS_RECORD; + fseek(gf->gf_file, retval, SEEK_SET); + } + } + else + { + gf->gf_state = GFS_ERROR; + retval = 0; + } + gf->gf_addr = gf->gf_low; + break; + case GMON_TAG_CG_ARC: + retval = ftell(gf->gf_file); + /* If this is a rewrite seek back to the record */ + if( rewrite != -1 ) + fseek(gf->gf_file, rewrite, SEEK_SET); + fwrite(&cga, sizeof(cga), 1, gf->gf_file); + if( !ferror(gf->gf_file) ) + { + /* + * If it was a rewrite, seek forward to where + * we were + */ + if( rewrite != -1 ) + fseek(gf->gf_file, retval, SEEK_SET); + } + else + { + gf->gf_state = GFS_ERROR; + retval = 0; + } + break; + default: + break; + } + } + else + { + gf->gf_state = GFS_ERROR; + } + va_end(args); + return( retval ); +} + +int writeGmonSamples(struct gmon_file *gf, char *addr, short *bins, int size) +{ + int retval = 1; + + assert(gf->gf_state == GFS_SAMPLES); + /* Fill any gaps between the current address and the one we're given */ + retval = fillGmonSamples(gf, addr); + if( retval ) + { + fwrite(bins, sizeof(short), size, gf->gf_file); + if( !ferror(gf->gf_file) ) + { + /* Increment the current sample index */ + gf->gf_addr += (size * HISTFRACTION); + if( gf->gf_addr == gf->gf_high ) + { + /* Switch back to writing records */ + gf->gf_state = GFS_RECORD; + } + else + { + assert( gf->gf_addr < gf->gf_high ); + } + } + else + { + gf->gf_state = GFS_ERROR; + retval = 0; + } + } + else + gf->gf_state = GFS_ERROR; + return( retval ); +} + +int fillGmonSamples(struct gmon_file *gf, char *addr) +{ + short filler = 0; + int retval = 1; + + /* If there was a gap in writing the samples then fill it with zeros */ + for( ; (gf->gf_addr < addr) && retval; gf->gf_addr += HISTFRACTION ) + { + fwrite(&filler, sizeof(filler), 1, gf->gf_file); + } + if( !ferror(gf->gf_file) ) + { + /* Switch back to writing records */ + if( gf->gf_addr == gf->gf_high ) + gf->gf_state = GFS_RECORD; + } + else + gf->gf_state = GFS_ERROR; + return( retval ); +} + +int gmonSampleWalker(void *handle, char *addr, short *bins, int size) +{ + struct gmon_file *gf = handle; + + return( !writeGmonSamples(gf, addr, bins, size) ); +} + +#endif /* KAFFE_XPROFILER */ Index: kaffe/kaffe/xprof/gmonFile.h diff -u /dev/null kaffe/kaffe/xprof/gmonFile.h:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/gmonFile.h Thu May 4 17:08:14 2000 @@ -0,0 +1,99 @@ +/* + * gmonFile.h + * gmon_out file handling routines + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#ifndef __gmonfile_h +#define __gmonfile_h + +#include +#include + +#include "gmon_out.h" + +/* States for a gmon_file */ +enum { + GFS_NONE, /* Just a barrier */ + GFS_ERROR, /* There was an error writing the file */ + GFS_RECORD, /* Adding records */ + GFS_SAMPLES, /* Writing samples for a hist record */ +}; + +struct gmon_file { + char *gf_name; /* The file name */ + FILE *gf_file; /* The file handle */ + char *gf_low; /* Lowest sample (usually the PC) for the file */ + char *gf_addr; /* Tracks current sample when writing out the file */ + char *gf_high; /* Highest sample for the file */ + int gf_state; /* State of the file */ +}; + +/* Tags for writeGmonRecord() */ +enum { + GRA_DONE, /* () Terminates the tag list */ + + GRA_Type, /* + * (enum { + * GMON_TAG_TIME_HIST, + * GMON_TAG_CG_ARG, + * GMON_TAG_BB_COUNT}) Type of record to + * add + */ + GRA_Rewrite, /* (long) Rewrite the record at the index */ + + GRA_LowPC, /* (char *) Lowest PC for a histogram */ + GRA_HighPC, /* (char *) Highest PC for a histogram */ + GRA_PCSize, /* + * (unsigned int) Sets the highest PC to + * GRA_LowPC + GRA_PCSize + */ + GRA_Dimension, /* (string) The physical dimension */ + GRA_DimensionAbbrev, /* (char) The abbreviation of the dimension */ + GRA_ProfilingRate, /* The profiling rate */ + + GRA_FromPC, /* (char *) The from PC for a call arc */ + GRA_SelfPC, /* (char *) The self PC for a call arc */ + GRA_Count, /* (int) The number of arc traversals */ +}; + +/* + * Create a gmon_file structure, also opens the given file for writing. + */ +struct gmon_file *createGmonFile(char *filename); +/* + * Delete a gmon_file structure, this also closes the file. + */ +void deleteGmonFile(struct gmon_file *gf); +/* + * Write a record to the gmon file, if the record is a histogram header, than + * it needs to be followed by a call to writeGmonSamples, to write the samples + * out. + */ +long writeGmonRecord(struct gmon_file *gf, int tag, ...); +/* + * Write out a subset of the histograms to the gmon file. If `addr' is not + * contiguous with previous calls to this function the gap will be filled + * in with zeroes. + */ +int writeGmonSamples(struct gmon_file *gf, char *addr, short *bins, int count); +/* + * A walker function for walking over a memory profile and calling + * writeGmonSamples. + */ +int gmonSampleWalker(void *handle, char *addr, short *bins, int size); +/* + * Fill the gap up to `addr' with zeros in the file. + */ +int fillGmonSamples(struct gmon_file *gf, char *addr); + +#endif /* __gmonfile_h */ Index: kaffe/kaffe/xprof/gmon_out.h diff -u /dev/null kaffe/kaffe/xprof/gmon_out.h:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/gmon_out.h Thu May 4 17:08:16 2000 @@ -0,0 +1,51 @@ +/* + * This file specifies the format of gmon.out files. It should have + * as few external dependencies as possible as it is going to be + * included in many different programs. That is, minimize the + * number of #include's. + * + * A gmon.out file consists of a header (defined by gmon_hdr) followed + * by a sequence of records. Each record starts with a one-byte tag + * identifying the type of records, followed by records specific data. + */ +#ifndef gmon_out_h +#define gmon_out_h + +#define GMON_MAGIC "gmon" /* magic cookie */ +#define GMON_VERSION 1 /* version number */ + +/* + * Raw header as it appears on file (without padding): + */ +struct gmon_hdr + { + char cookie[4]; + char version[4]; + char spare[3 * 4]; + }; + +/* types of records in this file: */ +typedef enum + { + GMON_TAG_TIME_HIST = 0, GMON_TAG_CG_ARC = 1, GMON_TAG_BB_COUNT = 2 + } +GMON_Record_Tag; + +struct gmon_hist_hdr + { + char low_pc[sizeof (char*)]; /* base pc address of sample buffer */ + char high_pc[sizeof (char*)]; /* max pc address of sampled buffer */ + char hist_size[4]; /* size of sample buffer */ + char prof_rate[4]; /* profiling clock rate */ + char dimen[15]; /* phys. dim., usually "seconds" */ + char dimen_abbrev; /* usually 's' for "seconds" */ + }; + +struct gmon_cg_arc_record + { + char from_pc[sizeof (char*)]; /* address within caller's body */ + char self_pc[sizeof (char*)]; /* address within callee's body */ + char count[4]; /* number of arc traversals */ + }; + +#endif /* gmon_out_h */ Index: kaffe/kaffe/xprof/mangle.c diff -u /dev/null kaffe/kaffe/xprof/mangle.c:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/mangle.c Thu May 4 17:08:17 2000 @@ -0,0 +1,576 @@ +/* + * mangle.c + * Routines for doing name mangling on Java types + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#include +#include +#include + +#include "jmalloc.h" +#include "stringSupport.h" +#include "classMethod.h" +#include "xprofiler.h" + +#include "mangle.h" + +struct mangled_method *createMangledMethod(void) +{ + struct mangled_method *retval; + + xProfilingOff(); + if( (retval = (struct mangled_method *) + KMALLOC(sizeof(struct mangled_method))) ) + { + retval->mm_flags = 0; + retval->mm_method = 0; + retval->mm_class = 0; + retval->mm_args = 0; + retval->mm_nargs = 0; + } + xProfilingOn(); + return( retval ); +} + +void deleteMangledMethod(struct mangled_method *mm) +{ + xProfilingOff(); + if( mm ) + { + int lpc; + + KFREE(mm->mm_method); + KFREE(mm->mm_class); + for( lpc = 0; lpc < mm->mm_nargs; lpc++ ) + { + KFREE(mm->mm_args[lpc]); + } + KFREE(mm->mm_args); + KFREE(mm); + } + xProfilingOn(); +} + +int mangleMethodName(struct mangled_method *mm, char *name) +{ + int retval = 0, len, m_len; + + /* Constructors are mangled as an empty string */ + if( !strcmp(name, "") ) + { + name = ""; + } + len = strlen(name); + if( (m_len = mangleLength(name, len, 0, 0)) ) + { + /* + * A method name with special chars has the `U' placed at the + * end + */ + mm->mm_flags |= MMF_UNICODE_METHOD; + } + else + m_len = len; + if( (mm->mm_method = (char *)KMALLOC(m_len + 1)) ) + { + int res; + + res = mangleString(mm->mm_method, name, len, m_len != len); + assert(res <= (m_len + 1)); + retval = 1; + } + return( retval ); +} + +int mangleMethodClass(struct mangled_method *mm, void *cl, char *name) +{ + int retval = 0; + + /* Just mangle the type directly */ + if( (mm->mm_class = mangleClassType(0, cl, name)) ) + { + retval = 1; + } + return( retval ); +} + +int mangleMethodArgCount(struct mangled_method *mm, int count) +{ + int retval = 0; + + if( !count || + (mm->mm_args = (char **)KMALLOC(sizeof(char *) * count)) ) + { + mm->mm_nargs = count; + retval = 1; + } + return( retval ); +} + +/* + * Helper function that checks for duplicate parameter types. + */ +static int duplicateParameter(Method *meth, int curr_param) +{ + int curr_len, lpc, retval = -1; + + /* Figure out the length of the curr_param type string */ + if( curr_param == meth->parsed_sig->nargs ) + { + /* + * Its the last arg, an ')' and the return type follow, so we + * use them to find the length + */ + curr_len = (meth->parsed_sig->ret_and_args[0] - 1) - + meth->parsed_sig->ret_and_args[curr_param]; + } + else + { + curr_len = meth->parsed_sig->ret_and_args[curr_param] - + meth->parsed_sig->ret_and_args[curr_param + 1]; + } + /* + * Loop over the parameters searching for one that matches curr_param, + * we start at 1 since 0 is the return type. + */ + for( lpc = 1; (lpc < curr_param) && (retval != -1); lpc++ ) + { + int arg_len; + + /* Figure out the length of the current parameter type */ + if( lpc == meth->parsed_sig->nargs ) + { + arg_len = (meth->parsed_sig->ret_and_args[0] - 1) - + meth->parsed_sig->ret_and_args[lpc]; + } + else + { + arg_len = meth->parsed_sig->ret_and_args[lpc] - + meth->parsed_sig->ret_and_args[lpc + 1]; + } + if( arg_len > 1 ) + { + if( (strncmp(&meth->parsed_sig->signature-> + data[meth->parsed_sig-> + ret_and_args[curr_param]], + &meth->parsed_sig->signature-> + data[meth->parsed_sig->ret_and_args[lpc]], + arg_len) == 0) && + (curr_len == arg_len) ) + { + retval = lpc; + } + } + } + return( retval ); +} + +int mangleMethodArgs(struct mangled_method *mm, Method *meth) +{ + int retval = 1, lpc, ref; + + for( lpc = 1; lpc <= mm->mm_nargs; lpc++ ) + { + if( (ref = duplicateParameter(meth, lpc)) >= 0 ) + { + /* + * Duplicate parameter, use `T' to back ref the + * previous one + */ + if( (mm->mm_args[lpc - 1] = (char *) + KMALLOC(5)) ) + { + sprintf(mm->mm_args[lpc - 1], + "T%d%s", + ref, + (ref > 9) ? "_" : ""); + } + } + else + { + /* Unique parameter, mangle the type */ + mm->mm_args[lpc - 1] = mangleType( + 0, + (char *)&meth->parsed_sig->signature-> + data[meth->parsed_sig->ret_and_args[lpc]]); + } + } + return( retval ); +} + +int mangleMethod(struct mangled_method *mm, Method *meth) +{ + int retval = 0; + + xProfilingOff(); + /* Try to mangle everything provided by `meth' */ + if( mangleMethodName(mm, (char *)meth->name->data) && + mangleMethodClass(mm, + meth->class->loader, + (char *)CLASS_CNAME(meth->class)) && + mangleMethodArgCount(mm, meth->parsed_sig->nargs) && + mangleMethodArgs(mm, meth) ) + { + retval = 1; + } + xProfilingOn(); + return( retval ); +} + +int printMangledMethod(struct mangled_method *mm, FILE *file) +{ + int retval = 0; + + /* Atleast check for method and class */ + if( mm && + mm->mm_method && + mm->mm_class ) + { + int lpc; + + retval = 1; + fprintf(file, "%s__%s", mm->mm_method, mm->mm_class); + for( lpc = 0; (lpc < mm->mm_nargs) && retval; lpc++ ) + { + if( mm->mm_args[lpc] ) + fprintf(file, "%s", mm->mm_args[lpc]); + else + retval = 0; + } + /* + * If the method name has escapes we need to append the `U' to + * the end + */ + if( mm->mm_flags & MMF_UNICODE_METHOD ) + fprintf(file, "U"); + if( ferror(file) ) + retval = 0; + } + return( retval ); +} + +/* Map of primitive Java types to the mangled types */ +static char *primitive_type_map[] = { + "Z", "b", /* boolean */ + "C", "w", /* wide char */ + "V", "v", /* void */ + "B", "c", /* byte */ + "S", "s", /* short */ + "I", "i", /* integer */ + "J", "x", /* long */ + "F", "f", /* float */ + "D", "d", /* double */ + 0 +}; + +char *manglePrimitiveType(char type) +{ + char *retval = 0; + int lpc; + + for( lpc = 0; primitive_type_map[lpc] && !retval; lpc += 2 ) + { + if( type == primitive_type_map[lpc][0] ) + retval = primitive_type_map[lpc + 1]; + } + return( retval ); +} + +char *mangleClassType(int prepend, void *cl, char *name) +{ + int quals = 0, num_chars = 0, num_underscores = 0, need_escapes = 0; + int ch, len, m_len = 0, error = 0, total_len = 0; + char *retval = 0, *curr, *end; + + /* First we find the length of mangled string */ + len = strlen(name); + curr = name; + end = name + len; + while( (curr < end) && !error ) + { + ch = UTF8_GET(curr, end); + if( ch < 0 ) + { + error = 1; + } + else if( ch == ';' ) + { + /* + * The given name was of the form Ljava/lang/Object;, + * so the `;' marks the end instead of the given null + */ + end = curr - 1; + break; + } + else if( ch == '/' ) + { + /* + * Its a qualified name, record the current counts for + * this name segment and increment the number of + * qualifiers + */ + quals++; + m_len += 4 + (need_escapes ? 7 : 0) + num_chars + + 4 * (need_escapes + num_underscores); + num_chars = 0; + need_escapes = 0; + num_underscores = 0; + } + else if( (ch >= '0') && (ch <= '9') ) + { + /* If a number starts a name then we need an escape */ + if( num_chars == 0 ) + need_escapes++; + } + else if( ch == '_' ) + { + num_underscores++; + } + else if( ((ch < 'a') || (ch > 'z')) && + ((ch < 'A') || (ch > 'Z')) ) + { + /* Its a special char, we'll need an escape */ + need_escapes++; + } + num_chars++; + } + /* Figure out the total length of the mangled name */ + total_len = m_len + 4 + (need_escapes ? 7 : 0) + + (quals ? 7 : 0) + num_chars + + 4 * (need_escapes + num_underscores); + /* + * If the class uses a non-root classLoader we need to encode that in + * the name, otherwise we can make duplicate names for the same class + * that are loaded by different class loaders. + */ + if( cl ) + { + total_len += (quals ? 0 : 7) + + 2 + /* character count of `cl' + the number */ + 2 + /* 'cl' */ + 2 + /* '0x' */ + (sizeof(void *) * 2); /* the number */ + quals++; + } + if( !error && (retval = (char *)KMALLOC(prepend + total_len + 1)) ) + { + char *dest; + + /* Start after the prepended section */ + dest = retval + prepend; + dest[0] = 0; + if( quals ) + { + /* + * Its a qualified name, print out how many qualifiers + * there are before continuing + */ + quals++; + if( quals < 10 ) + sprintf(dest, "Q%d", quals); + else + sprintf(dest, "Q_%d_", quals); + quals--; + } + dest += strlen(dest); + /* Encode the class loader, if there is one */ + if( cl ) + { + int cl_len; + + sprintf(dest + 3, "l%p", cl); + cl_len = strlen(dest + 3) + 1; + sprintf(dest, "%d", cl_len); + dest[2] = 'c'; /* The previous sprintf overwrote it */ + dest += cl_len + 2; + quals--; + } + curr = name; + while( curr < end ) + { + /* Figure out the length of this name segment */ + if( (m_len = mangleLength(curr, + quals ? -1 : end - curr, + '/', + &len)) ) + { + *dest = 'U'; + dest++; + } + else + m_len = len; + /* Write the length of the name */ + sprintf(dest, "%d", m_len); + dest += strlen(dest); + /* Mangle the string */ + mangleString(dest, curr, len, m_len != len); + /* Move on to the next name */ + dest += strlen(dest); + curr += len + 1; + quals--; + } + assert((dest - retval) <= (prepend + total_len + 1)); + } + return( retval ); +} + +char *mangleType(int prepend, char *type) +{ + char *retval = 0; + + switch(type[0]) + { + case 'L': + /* Object reference */ + if( (retval = mangleClassType(prepend + 1, 0, type + 1)) ) + retval[prepend] = 'P'; + break; + case '[': + /* Array type */ + if( (retval = mangleType(prepend + 11, type + 1)) ) + strncpy(&retval[prepend], "Pt6JArray1Z", 11); + break; + default: + /* Most likely a primitive */ + { + char *prim; + + if( (prim = manglePrimitiveType(type[0])) ) + { + if( (retval = (char *)KMALLOC(prepend + 2)) ) + { + retval[prepend] = prim[0]; + retval[prepend + 1] = 0; + } + } + } + break; + } + return( retval ); +} + +int mangleLength(char *string, int len, char term, int *out_len) +{ + int num_chars = 0, need_escapes = 0, num_underscores = 0; + int retval = -1, error = 0; + char *curr, *end; + + curr = string; + if( len < 0 ) + end = (char *)-1; /* ick */ + else + end = string + len; + while( !error && ((len < 0) || (curr < end)) ) + { + int ch = UTF8_GET(curr, end); + + if( ch < 0 ) + { + error = 1; + } + else if( ch == term ) + { + /* Found the specified terminator */ + break; + } + else if( (ch >= '0') && (ch <= '9') ) + { + /* If a number starts a name then we need an escape */ + if( num_chars == 0 ) + need_escapes++; + } + else if( ch == '_' ) + { + num_underscores++; + } + else if( ((ch < 'a') || (ch > 'z')) && + ((ch < 'A') || (ch > 'Z')) ) + { + /* Special character, we'll need an escape */ + need_escapes++; + } + num_chars++; + } + if( !error ) + { + if( need_escapes ) + { + retval = num_chars + + 4 * (need_escapes + num_underscores); + } + else + retval = 0; + /* Write back the length */ + if( out_len ) + *out_len = num_chars; + } + return( retval ); +} + +int mangleString(char *dest, char *src, int slen, int unicode) +{ + int retval = 0, ch, error = 0, need_escape = 0; + char *start, *curr, *end; + + start = dest; + curr = src; + end = src + slen; + while( (curr < end) && !error ) + { + ch = UTF8_GET(curr, end); + if( ch < 0 ) + { + error = 1; + } + else if( (ch >= '0') && (ch <= '9') ) + { + if( curr == (src - 1) ) + need_escape = 1; + else + need_escape = 0; + } + else if( ch == '_' ) + { + if( unicode ) + need_escape = 1; + } + else if( ((ch < 'a') || (ch > 'z')) && + ((ch < 'A') || (ch > 'Z')) ) + { + need_escape = 1; + } + else + { + need_escape = 0; + } + if( !error ) + { + if( need_escape ) + { + sprintf(dest, "_%04x", ch); + dest += 5; + } + else + { + *dest = ch; + dest++; + } + } + } + *dest = 0; + if( error ) + retval = -1; + else + retval = dest - start + 1; + return( retval ); +} Index: kaffe/kaffe/xprof/mangle.h diff -u /dev/null kaffe/kaffe/xprof/mangle.h:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/mangle.h Thu May 4 17:08:18 2000 @@ -0,0 +1,104 @@ +/* + * mangle.h + * Routines for doing name mangling on Java types + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#ifndef __mangle_h +#define __mangle_h + +#include + +#include "classMethod.h" + +/* Bit numbers for the flags in the mangled_method structure */ +enum { + MMB_UNICODE_METHOD, /* Indicates the method name has utf chars */ +}; + +/* Flags for the mangled_method structure */ +enum { + MMF_UNICODE_METHOD = (1L << MMB_UNICODE_METHOD), +}; + +/* + * Root structure for storing a mangled method signature + */ +struct mangled_method { + unsigned long mm_flags; /* Flags for the structure */ + char *mm_method; /* The mangled method name */ + char *mm_class; /* The mangled class name */ + char **mm_args; /* Array of mangled args */ + int mm_nargs; /* Number of args */ +}; + +/* + * Allocate and initialize the root mangled_method structure + */ +struct mangled_method *createMangledMethod(void); +/* + * Delete the mangled_method structure and any referenced memory + */ +void deleteMangledMethod(struct mangled_method *mm); +/* + * Mangle the given method name and place it in the structure + */ +int mangleMethodName(struct mangled_method *mm, char *name); +/* + * Mangle the given class name and place it in the structure + */ +int mangleMethodClass(struct mangled_method *mm, void *cl, char *name); +/* + * Set the number of args for this mangled method + */ +int mangleMethodArgCount(struct mangled_method *mm, int count); +/* + * Mangle the arguments in the given method + */ +int mangleMethodArgs(struct mangled_method *mm, Method *meth); +/* + * Mangle a whole method and store it in the given mangled_method structure + */ +int mangleMethod(struct mangled_method *mm, Method *meth); +/* + * Print the mangled method structure to the given file + */ +int printMangledMethod(struct mangled_method *mm, FILE *file); +/* + * Mangle a primitive Java type, returns a statically allocated string that + * has the proper mangled value. + */ +char *manglePrimitiveType(char type); +/* + * Mangle the given class name and return a KMALLOC'ed buffer with the string + * and `prepend' extra bytes in front of the string. (Note: the class name + * needs to be in internal form, e.g. java/lang/Object.) + */ +char *mangleClassType(int prepend, void *cl, char *name); +/* + * Mangle a type description and return a KMALLOC'ed buffer with the mangled + * string and `prepend' extra bytes at the front. + */ +char *mangleType(int prepend, char *type); +/* + * Mangle string `src' of length `len', and place the result into `dest' + */ +int mangleString(char *dest, char *src, int slen, int unicode); +/* + * Determine the mangled length of the given string. If the string doesn't + * require any escapes then zero is returned. If len is -1 then `term' is + * taken as the terminating character. If `out_len' is non-NULL then it is + * always set to the unmangled length of `string'. + */ +int mangleLength(char *string, int len, char term, int *out_len); + +#endif /* __mangle_h */ Index: kaffe/kaffe/xprof/memorySamples.c diff -u /dev/null kaffe/kaffe/xprof/memorySamples.c:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/memorySamples.c Thu May 4 17:08:19 2000 @@ -0,0 +1,403 @@ +/* + * memorySamples.c + * Routines for tracking locations in memory when profiling + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#include +#include +#include + +#include "jmalloc.h" + +#include "memorySamples.h" + +/* Bits used to determine the branch */ +#define SAMPLE_BIT_COUNT 8 +#define SAMPLE_BIT_MASK 0xff + +/* Number of branches at a level in the tree */ +#define SAMPLE_BRANCH_COUNT 256 + +/* Number of leaves to store sample counts */ +#define SAMPLE_LEAF_COUNT (SAMPLE_BRANCH_COUNT / 2) + +/* Address size */ +#define SAMPLE_ADDRESS_BYTES SIZEOF_VOIDP +#define SAMPLE_ADDRESS_BITS (SAMPLE_ADDRESS_BYTES * 8) + +#define SAMPLE_BRANCH_LEVELS (SAMPLE_ADDRESS_BYTES - 1) + +/* Macros for computing branches/leaf indexes */ +#define SAMPLE_BRANCH(addr, level) \ + ((((int)addr) >> (SAMPLE_ADDRESS_BITS - (SAMPLE_BIT_COUNT * \ + ((level) + 1)))) \ + & SAMPLE_BIT_MASK) +#define SAMPLE_BIN(addr) ((((int)addr) & 0xfe) >> 1) +/* Set the index for some level in a pointer value */ +#define SET_ADDR_LEVEL(addr, level, value) \ + ((void *)((((int)(addr)) & \ + ~(SAMPLE_BIT_MASK << \ + (((SAMPLE_BRANCH_LEVELS) - (level)) * \ + SAMPLE_BIT_COUNT))) | \ + (((int)(value)) << \ + (((SAMPLE_BRANCH_LEVELS) - (level)) * \ + SAMPLE_BIT_COUNT)))) +/* Align the address to something that's sample-able */ +#define ALIGN_ADDR(addr) ((char *)((int)((addr) + 2) & ~1)) + +#define min(x,y) ((x < y) ? x : y) +#define max(x,y) ((x > y) ? x : y) + +/* Allocates and zeroes out memory for the sample bins */ +static short *createSampleLeaves(void) +{ + short *retval; + + retval = (short *)KMALLOC(sizeof(short) * SAMPLE_LEAF_COUNT); + return( retval ); +} + +/* Free memory allocated for the leaves */ +static void deleteSampleLeaves(short *leaves) +{ + KFREE(leaves); +} + +/* Allocates and zeroes out memory for the sample tree pointers */ +static void **createSampleBranches(void) +{ + void **retval = 0; + + retval = (void **)KMALLOC(sizeof(void *) * SAMPLE_BRANCH_COUNT); + return( retval ); +} + +/* + * Delete a subtree of the sample branches, level indicates what level of the + * tree we are on so we know when we've hit the leaves. + */ +static void deleteSampleBranches(void **ptr, int level) +{ + int lpc; + + for( lpc = 0; lpc < SAMPLE_BRANCH_COUNT; lpc++ ) + { + if( ptr[lpc] ) + { + if( (level + 1) == SAMPLE_BRANCH_LEVELS ) + deleteSampleLeaves((short *)ptr[lpc]); + else + deleteSampleBranches(ptr[lpc], level + 1); + } + } +} + +struct memory_samples *createMemorySamples(void) +{ + struct memory_samples *retval; + + /* Allocate the root structure and the root branch pointers */ + if( (retval = (struct memory_samples *) + KMALLOC(sizeof(struct memory_samples))) && + (retval->ms_samples = createSampleBranches()) ) + { + retval->ms_flags = 0; + retval->ms_low = ((char *)0) - 1; + retval->ms_high = 0; + retval->ms_misses = 0; + } + else + { + KFREE(retval); + } + return( retval ); +} + +void deleteMemorySamples(struct memory_samples *ms) +{ + if( ms ) + { + /* Free the tree */ + deleteSampleBranches(ms->ms_samples, 0); + KFREE(ms); + } +} + +int observeMemory(struct memory_samples *ms, char *addr, int size) +{ + int retval = 1; + + if( ms->ms_flags & MSF_CONTIGUOUS ) + { + /* Its a contiguous block of memory */ + if( addr >= ms->ms_low ) + { + if( (addr + size) < ms->ms_high ) + { + } + else + { + void *samples; + + if( (samples = KREALLOC(ms->ms_samples, + ((addr + size) - + ms->ms_low) / + sizeof(short))) ) + { + ms->ms_high = addr + size; + ms->ms_samples = samples; + } + else + { + retval = 0; + } + } + } + else + { + void *samples; + + if( (samples = KREALLOC(ms->ms_samples, + (ms->ms_high - addr) / + sizeof(short))) ) + { + ms->ms_low = addr; + ms->ms_samples = samples; + } + } + } + else + { + /* else... The bins are organized in a tree */ + void **level[SAMPLE_BRANCH_LEVELS]; + int slot[SAMPLE_BRANCH_LEVELS]; + + ms->ms_low = min(ms->ms_low, addr); + ms->ms_high = max(ms->ms_high, ALIGN_ADDR(addr + size)); + + /* + * If the starting address doesn't fall directly on the + * boundary between leaves then we need to adjust `size' to + * incorporate the extra space. + */ + size += SAMPLE_BRANCH(addr, SAMPLE_BRANCH_LEVELS); + + /* + * Starting at the root we walk over the tree allocating + * necessary branches and leaves + */ + level[0] = ms->ms_samples; + /* Loop until we've allocated everything */ + while( (size > 0) && retval ) + { + int lpc; + + /* Process the address to get the branch indexes */ + for( lpc = 0; lpc < SAMPLE_BRANCH_LEVELS; lpc++ ) + { + slot[lpc] = SAMPLE_BRANCH(addr, lpc); + } + /* + * Walk through the upper levels and allocate any + * needed branches. + */ + for( lpc = 0; + (lpc < (SAMPLE_BRANCH_LEVELS - 1)) && retval; + lpc++ ) + { + if( !level[lpc][slot[lpc]] ) + { + /* No branch has been allocated yet */ + level[lpc][slot[lpc]] = + createSampleBranches(); + level[lpc + 1] = level[lpc][slot[lpc]]; + if( !level[lpc] ) + retval = 0; + } + else + { + level[lpc + 1] = level[lpc][slot[lpc]]; + } + } + /* Check if we need to allocate the leaf */ + if( retval && !level[lpc][slot[lpc]] ) + { + if( (level[lpc][slot[lpc]] = + createSampleLeaves()) ) + { + } + else + { + retval = 0; + } + } + /* Move on the next subrange */ + size -= SAMPLE_BRANCH_COUNT; + addr += SAMPLE_BRANCH_COUNT; + } + } + return( retval ); +} + +void memoryHit(struct memory_samples *ms, char *addr) +{ + void **level; + short *sptr; + int lpc; + + if( (addr >= ms->ms_low) && (addr <= ms->ms_high) ) + { + /* Walk the tree */ + level = ms->ms_samples; + for( lpc = 0; (lpc < SAMPLE_BRANCH_LEVELS) && level; lpc++ ) + { + level = level[SAMPLE_BRANCH(addr, lpc)]; + } + if( (sptr = (short *)level) ) + { + /* Theres a leaf, increment the counter */ + sptr[SAMPLE_BIN(addr)]++; + } + else + { + /* No leaf, record it is a miss */ + ms->ms_misses++; + } + } +} + +void memoryHitCount(struct memory_samples *ms, char *addr, int count) +{ + void **level; + short *sptr; + int lpc; + + if( (addr >= ms->ms_low) && (addr <= ms->ms_high) ) + { + /* Walk the tree */ + level = ms->ms_samples; + for( lpc = 0; (lpc < SAMPLE_BRANCH_LEVELS) && level; lpc++ ) + { + level = level[SAMPLE_BRANCH(addr, lpc)]; + } + if( (sptr = (short *)level) ) + { + /* Theres a leaf, increment the counter */ + sptr[SAMPLE_BIN(addr)] += count; + } + else + { + /* No leaf, record it is a miss */ + ms->ms_misses += count; + } + } +} + +/* + * Recurses over the tree and calls the walker function on the leaf nodes. + */ +static int walkHelper(struct memory_samples *ms, char **addr, void *handle, + sample_walker_t walker, void **branches, int level) +{ + int retval = 0, lpc; + + if( level == SAMPLE_BRANCH_LEVELS ) + { + /* We're at a leaf, call the walker function */ + retval = walker(handle, + *addr, + (short *)branches, + min(ms->ms_high - (*addr), + SAMPLE_BRANCH_COUNT - + SAMPLE_BRANCH(*addr, + SAMPLE_BRANCH_LEVELS)) / 2); + } + else + { + /* + * We're somewhere in the tree, start at whatever address is + * there initially and move from there. + */ + for( lpc = SAMPLE_BRANCH(*addr, level); + (lpc < SAMPLE_BRANCH_COUNT) && !retval; + lpc++ ) + { + if( branches[lpc] ) + { + *addr = SET_ADDR_LEVEL(*addr, + level, + lpc); + retval = walkHelper(ms, + addr, + handle, + walker, + branches[lpc], + level + 1); + } + /* Clear out the initial value */ + *addr = SET_ADDR_LEVEL(*addr, level + 1, 0); + } + } + return( retval ); +} + +void walkMemorySamples(struct memory_samples *ms, char *addr, void *handle, + sample_walker_t walker) +{ + walkHelper(ms, &addr, handle, walker, ms->ms_samples, 0); +} + +/* A simple walker function that zeroes out the sample values */ +static int resetBinsWalker(void *handle, char *addr, short *bins, int size) +{ + memset(bins, 0, size * sizeof(short)); + return( 0 ); +} + +void resetMemorySamples(struct memory_samples *ms) +{ + walkMemorySamples(ms, 0, 0, resetBinsWalker); + ms->ms_misses = 0; +} + +/* A simple walker function that prints out the bin values */ +static int printBinsWalker(void *handle, char *addr, short *bins, int size) +{ + FILE *file = handle; + int printed_header = 0; + int lpc; + + for( lpc = 0; lpc < size; lpc++ ) + { + if( bins[lpc] ) + { + if( !printed_header ) + { + /* Print out a header message */ + fprintf(file, "bins from %p to %p:\n", + addr, + addr + SAMPLE_BRANCH_COUNT); + printed_header = 1; + } + fprintf(file, " %p: %d\n", + addr + (lpc * 2), + bins[lpc]); + } + } + return( 0 ); +} + +void printMemorySamples(FILE *file, struct memory_samples *ms) +{ + walkMemorySamples(ms, 0, file, printBinsWalker); +} Index: kaffe/kaffe/xprof/memorySamples.h diff -u /dev/null kaffe/kaffe/xprof/memorySamples.h:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/memorySamples.h Thu May 4 17:08:23 2000 @@ -0,0 +1,82 @@ +/* + * memorySamples.h + * Routines for tracking locations in memory when profiling + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#ifndef __memorysamples_h +#define __memorysamples_h + +#include + +/* Bit numbers for various flags */ +enum { + MSB_CONTIGUOUS, /* The sample memory is contiguous */ +}; + +/* Flags for the memory_samples structure */ +enum { + MSF_CONTIGUOUS = 1L << MSB_CONTIGUOUS, +}; + +/* Root structure that holds references to the sample bins */ +struct memory_samples { + unsigned int ms_flags; /* Flags */ + char *ms_low; /* Low water mark for samples */ + char *ms_high; /* High water mark for samples */ + unsigned int ms_misses; /* Hits that fell outside the observed range */ + void *ms_samples; /* The root of the samples tree */ +}; + +/* + * Typedef for a function that is called while walking over the samples tree. + * The function will be called for each sample bin reached, `addr' refers + * to the beginning address that the `bins' cover. Size is the number of + * actual bins. + */ +typedef int (*sample_walker_t)(void *handle, char *addr, + short *bins, int size); + +/* + * Create a memory_samples structure. + */ +struct memory_samples *createMemorySamples(void); +/* + * Delete a memory_samples structure. + */ +void deleteMemorySamples(struct memory_samples *ms); +/* + * Allocate space in the sample tree to hold counters for the specified memory + * range + */ +int observeMemory(struct memory_samples *ms, char *addr, int size); +/* + * Increment the counter in the sample tree for the given address, if the + * address isn't being observed then the misses counter is increased. + */ +void memoryHit(struct memory_samples *ms, char *addr); +void memoryHitCount(struct memory_samples *ms, char *addr, int count); +/* + * Walk over the sample tree calling the walker function for each bin reached + */ +void walkMemorySamples(struct memory_samples *ms, char *addr, void *handle, + sample_walker_t walker); +/* + * Reset memory sample values + */ +void resetMemorySamples(struct memory_samples *ms); +/* + * Print out the memory sample tree. + */ +void printMemorySamples(FILE *file, struct memory_samples *ms); + +#endif /* __memorysamples_h */ Index: kaffe/kaffe/xprof/sectionFile.c diff -u /dev/null kaffe/kaffe/xprof/sectionFile.c:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/sectionFile.c Thu May 4 17:08:24 2000 @@ -0,0 +1,1214 @@ +/* + * sectionFile.c + * Routines for reading/writing a formatted text file + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "jmalloc.h" +#include "stringSupport.h" + +#include "sectionFile.h" + +/* Number of buckets for the file_section hash table */ +#define FILE_SECTION_TYPES_SIZE 31 + +/* Hash table for file_section's */ +static struct file_section *file_section_types[FILE_SECTION_TYPES_SIZE]; + +static void restoreLine(struct parse_state *ps); +static int syncFile(struct parse_state *parent, struct section_file *sf, + char *filename); + +/* + * Internal state for the file parser + */ +struct parse_state { + struct parse_state *ps_parent; /* Keep track of nesting */ + char *ps_filename; /* Current file name */ + struct section_file *ps_section_file; /* Root section file */ + FILE *ps_in_file; /* Input File handle */ + FILE *ps_out_file; /* Output File handle */ + struct section_file_data *ps_section; /* Current section */ + int ps_line; /* Current line number */ + int ps_column; /* Current column number */ + char *ps_data; /* Current line string */ + int ps_len; /* Length of line */ + char ps_save[2]; /* Storage for damage */ +}; + +/* + * Produce a hash value within table_size, for string `name' + */ +static int hashName(const char *name, int table_size) +{ + int h; + + for( h = 0; *name; name++ ) + h = (64 * h + tolower(*name)) % table_size; + return( h ); +} + +/* + * Check for valid characters in a string from the file + */ +static int validStringChar(int c) +{ + int retval = 0; + + if( isprint(c) && + (c != '#') && + (c != '%') && + (c != '\\') ) + retval = 1; + return( retval ); +} + +/* + * Write out a utf string with the proper escapes, if needed. + */ +static int writeUtfString(struct parse_state *ps, char *ustr) +{ + int ch, len, error = 0; + char *curr, *end; + + len = strlen(ustr); + end = ustr + len; + curr = ustr; + while( (curr < end) && !error ) + { + ch = UTF8_GET(curr, end); + if( ch < 0 ) + { + error = 1; + } + else if( validStringChar(ch) ) + { + /* Plain character, just write it out */ + fprintf(ps->ps_out_file, "%c", (char)ch); + } + else + { + /* Write out a UTF8 character with escape sequence */ + fprintf(ps->ps_out_file, "\\u%04x", ch); + } + } + return( !error ); +} + +enum { + SFS_INVALID, + SFS_PLAIN, + SFS_UTF8, +}; + +/* + * Figure out what kind of string has come from a file. If it has UTF8 + * escapes we'll need to translate those. + */ +static int stringType(char *str) +{ + int retval = SFS_PLAIN, utf8char = 0; + + while( *str && (retval != SFS_INVALID) ) + { + if( *str == '\\' ) + { + /* Do a thorough check of the escape */ + if( (*(str + 1) == 'u') && + (sscanf(str + 2, "%x", &utf8char) == 1) ) + retval = SFS_UTF8; + else + retval = SFS_INVALID; + } + str++; + } + return( retval ); +} + +/* + * Convert a UTF8 escaped string from the file into an internal format. + */ +static char *convertUtfString(struct parse_state *ps, char *pstr) +{ + char *retval = 0; + int len; + + len = strlen(pstr); + /* We're allocating more than is really needed... */ + if( (retval = (char *)KMALLOC(len + 1)) ) + { + int lpc, utf8char; + char *curr; + + curr = retval; + for( lpc = 0; lpc < len; lpc++ ) + { + switch( pstr[lpc] ) + { + case '\\': + utf8char = 0; + lpc += 2; /* Skip the 'u' */ + if( sscanf(&pstr[lpc], + "%4x", + &utf8char) == 1 ) + { + char plain_char; + + /* + * XXX This isn't a proper check for a + * UTF char. We just check if its can + * be held within 8 bits and write it + * directly to the string, otherwise we + * store it as 16 bits. + */ + plain_char = utf8char; + if( plain_char == utf8char ) + { + *curr = plain_char; + } + else + { + *curr = utf8char >> 8; + curr++; + *curr = utf8char & 0xff; + } + curr++; + } + lpc += 3; /* Skip the value */ + break; + default: + /* Plain character, just copy it over */ + *curr = pstr[lpc]; + curr++; + break; + } + } + *curr = 0; + } + return( retval ); +} + +/* + * Do the actual removal of a section from a file, this is called after + * write sync when a section was scheduled for removal. + */ +static void reallyRemoveSectionFromFile(struct section_file *sf, + struct section_file_data *sfd) +{ + struct section_file_data **prev, *curr; + int hash; + + /* Remove it from the hash table */ + hash = hashName(sfd->sfd_name, SECTION_FILE_HASH_SIZE); + curr = sf->sf_sections[hash]; + prev = &sf->sf_sections[hash]; + while( curr && (curr != sfd) ) + { + prev = &curr->sfd_next; + curr = curr->sfd_next; + } + if( curr ) + *prev = sfd->sfd_next; + /* Remove it from the ordered list */ + curr = sf->sf_ordered_sections; + prev = &sf->sf_ordered_sections; + while( curr && (curr != sfd) ) + { + prev = &curr->sfd_order; + curr = curr->sfd_order; + } + if( curr ) + *prev = sfd->sfd_order; +} + +/* Parse a directive line */ +static int parseDirective(struct parse_state *ps, int curr) +{ + int retval = 1, lpc, directive_len, args_len, line_len; + char *directive, *args, *save, *line; + struct section_file *sf; + FILE *out_file; + + sf = ps->ps_section_file; + out_file = ps->ps_out_file; + line_len = ps->ps_len; + line = ps->ps_data; + save = ps->ps_save; + + /* The line passed in points to the beginning of the directive */ + directive = &line[curr]; + /* Move to the end of the directive */ + for( lpc = curr; (lpc < line_len) && !isspace(line[lpc]); lpc++ ); + directive_len = lpc - curr; + /* The arguments to the directive directly follow it */ + args = &line[lpc + 1]; + args_len = line_len - (lpc + 1); + if( !strncmp(directive, "%include", directive_len) ) + { + int name_start, name_end; + + /* Try and get a quoted filename from the args */ + for( name_start = 0; + (name_start < args_len) && (args[name_start] != '\"'); + name_start++ ); + for( name_end = name_start + 1; + (name_end < args_len) && (args[name_end] != '\"'); + name_end++ ); + if( (name_start < args_len) && (name_end < args_len) ) + { + args[name_end] = 0; + /* Try and sync the include file */ + if( !syncFile(ps, ps->ps_section_file, + args + name_start + 1) ) + { + fprintf(stderr, + "Error:%s:%d:%d - Unable to process " + "file %s.\n", + ps->ps_filename, + ps->ps_line, + ps->ps_column, + args + name_start + 1); + } + args[name_end] = '\"'; + } + else + { + fprintf(stderr, + "Error:%s:%d:%d - %%include directive needs " + "a quoted filename.\n", + ps->ps_filename, + ps->ps_line, + ps->ps_column); + } + if( out_file ) + { + fwrite(line, 1, line_len, out_file); + } + } + else if( !strncmp(directive, "%begin", directive_len) ) + { + char *section_type = 0, *section_name = 0; + struct file_section *type; + int stype; + + /* This is the beginning of a section */ + + /* Parse the args into type and name */ + ps->ps_data = args; + ps->ps_len = args_len; + parseSectionLine(ps, §ion_type, §ion_name, 0); + ps->ps_data = line; + ps->ps_len = line_len; + type = findSectionType(section_type); + switch( (stype = stringType(section_name)) ) + { + case SFS_INVALID: + retval = 0; + fprintf(stderr, + "Error:%s:%d - Invalid characters in section " + "name\n", + ps->ps_filename, + ps->ps_line); + break; + case SFS_PLAIN: + break; + case SFS_UTF8: + if( !(section_name = + convertUtfString(ps, section_name)) ) + retval = 0; + break; + } + if( !retval ) + { + } + else if( (ps->ps_section = findSectionInFile(sf, + type, + section_name)) ) + { + /* The section already exists */ + if( out_file ) + { + /* + * We'll be writing out the data in a bit + * so clear the dirty bit. + */ + ps->ps_section->sfd_flags &= ~SFDF_DIRTY; + } + } + else if( out_file ) + { + /* + * If we're writing and there's no section then + * something is screwy and we should probably cache + * the unseen data... or not + */ + } + else + { + /* + * The section hasn't been seen before, make a new + * one and add it to the structure. + */ + if( (ps->ps_section = createFileSection(section_type, + section_name, + NULL)) ) + { + addSectionToFile(sf, ps->ps_section); + } + else + { + retval = 0; + } + } + if( stype == SFS_UTF8 ) + KFREE(section_name); + /* Fix the damage parseSectionLine did */ + restoreLine(ps); + if( out_file && ps->ps_section && + !(ps->ps_section->sfd_flags & SFDF_REMOVED) ) + { + fwrite(line, 1, line_len, out_file); + } + } + else if( !strncmp(directive, "%end", directive_len) ) + { + /* This is the end of a section */ + if( out_file && ps->ps_section ) + { + if( ps->ps_section->sfd_flags & SFDF_REMOVED ) + { + /* The section was pending removal */ + reallyRemoveSectionFromFile(sf, + ps->ps_section); + deleteFileSection(ps->ps_section); + } + else + { + /* Ask the handler to write any new data out */ + retval = ps->ps_section->sfd_type-> + fs_handler(ps->ps_section->sfd_type, + sf, + SFM_FLUSH, + ps->ps_section, + ps, + 0, + 0, + out_file); + fwrite(line, 1, line_len, out_file); + } + } + /* We're out of the section so clear the current section */ + ps->ps_section = 0; + } + else + { + fprintf(stderr, + "Error:%s:%d - Directive `%s' is not valid\n", + ps->ps_filename, + ps->ps_line, + directive); + } + if( out_file && ferror(out_file) ) + retval = 0; + return( retval ); +} + +/* XXX bleh */ +#define MAX_LINE_SIZE 1024 + +/* + * Parse the input file and send any output to `out_file' if there is one + */ +static int parseFile(struct parse_state *ps) +{ + char *line, buffer[MAX_LINE_SIZE]; + int retval = 1, line_len; + FILE *in_file, *out_file; + struct section_file *sf; + + sf = ps->ps_section_file; + in_file = ps->ps_in_file; + out_file = ps->ps_out_file; + ps->ps_data = buffer; + + /* Walk over the input file line by line */ + while( retval && (line = fgets(buffer, MAX_LINE_SIZE, in_file)) ) + { + int curr; + + ps->ps_line++; + ps->ps_column = 0; + ps->ps_len = line_len = strlen(line); + /* Skip white space */ + for( curr = 0; + (curr < line_len) && isspace(line[curr]); + curr++ ) + ps->ps_column++; + if( curr == line_len ) + { + /* Empty line, dump to output */ + if( out_file ) + fwrite(line, 1, line_len, out_file); + continue; + } + /* Found something, parse it */ + switch( line[curr] ) + { + case '%': + /* Directive's start with %, handle it */ + retval = parseDirective(ps, curr); + curr = line_len; + break; + case '#': + /* A comment, dump the rest of the line to output */ + if( out_file ) + { + fwrite(line, 1, line_len, out_file); + } + curr = line_len; + break; + default: + if( ps->ps_section ) + { + if( ps->ps_section->sfd_flags & SFDF_REMOVED ) + continue; + /* + * We're in a section, ask the handler to + * process it + */ + if( !line_len || (line[line_len - 1] != '\n') ) + { + retval = 0; + } + else if( out_file ) + { + /* Bring the file up to date */ + retval = ps->ps_section->sfd_type-> + fs_handler(ps->ps_section-> + sfd_type, + sf, + SFM_FLUSH, + ps->ps_section, + ps, + line, + line_len, + out_file); + } + else + { + /* Read new data in */ + retval = ps->ps_section->sfd_type-> + fs_handler(ps->ps_section-> + sfd_type, + sf, + SFM_CACHE, + ps->ps_section, + ps, + line, + line_len); + } + } + else + { + fprintf(stderr, + "Error:%s:%d:%d - Text outside of " + "section\n", + ps->ps_filename, + ps->ps_line, + ps->ps_column); + if( out_file ) + { + /* + * Be nice and output it, but put it in + * a comment + */ + fprintf(out_file, "# %s", line); + } + } + curr = line_len; + break; + } + } + /* Check for errors */ + if( ferror(in_file) || (out_file && ferror(out_file)) ) + retval = 0; + return( retval ); +} + +/* + * Check for any new sections added to the section_file and output them to + * the file. + */ +static int writeNewSections(struct parse_state *ps) +{ + struct section_file_data *sfd; + struct section_file *sf; + int retval = 1; + FILE *out_file; + + sf = ps->ps_section_file; + out_file = ps->ps_out_file; + + /* Walk over the hash table links */ + sfd = sf->sf_ordered_sections; + while( sfd && retval ) + { + if( sfd->sfd_flags & SFDF_DIRTY ) + { + /* + * This section wasn't touched while sync'ing + * it must be new to the file. + */ + if( sfd->sfd_name[0] ) + { + fprintf(out_file, + "\n%%begin %s ", + sfd->sfd_type->fs_name); + retval = writeUtfString(ps, sfd->sfd_name); + fprintf(out_file, "\n"); + } + else + { + fprintf(out_file, + "\n%%begin %s\n", + sfd->sfd_type->fs_name); + } + retval = sfd->sfd_type->fs_handler(sfd->sfd_type, + sf, + SFM_FLUSH, + sfd, + ps, + 0, + 0, + out_file); + fprintf(out_file, "%%end\n"); + sfd->sfd_flags &= ~SFDF_DIRTY; + } + sfd = sfd->sfd_order; + } + if( ferror(out_file) ) + retval = 0; + return( retval ); +} + +/* + * Write out the internal data to the file + */ +static int writeFile(struct parse_state *ps) +{ + int retval = 0, temp_fd = -1; + FILE *in_file, *out_file; + struct section_file *sf; + char *temp_name = 0; + + sf = ps->ps_section_file; + in_file = ps->ps_in_file; + + /* Create a temporary file for the output */ + if( (temp_name = (char *)KMALLOC(10)) && + strcpy(temp_name, "sf.XXXXXX") && + ((temp_fd = mkstemp(temp_name)) >= 0) && + (out_file = fdopen(temp_fd, "w+")) ) + { + ps->ps_out_file = out_file; + /* Parse the old file and update with current data */ + if( (retval = parseFile(ps)) ) + { + if( !ps->ps_parent ) + { + /* + * Write out any sections that don't have peers + * in the input file + */ + retval = writeNewSections(ps); + } + } + else + { + fprintf(stderr, + "Error: Unable to sync file %s\n", + ps->ps_filename); + } + fclose(out_file); + /* + * If there weren't any errors we rename the temporary file + * so that it overwrites the input file. Otherwise, we remove + * the bad output file, and leave the input file intact. + */ + if( retval ) + { + rename(temp_name, ps->ps_filename); + } + else + { + remove(temp_name); + } + } + else + { + fprintf(stderr, + "Error: Unable to create temporary file for " + "rewriting %s.\n", + ps->ps_filename); + if( temp_fd >= 0 ) + { + remove(temp_name); + close(temp_fd); + } + } + KFREE(temp_name); + return( retval ); +} + +/* + * Internal file sync function. This allows for included files to be processed + * and their data contained in the root section file. + */ +static int syncFile(struct parse_state *parent, struct section_file *sf, + char *filename) +{ + struct stat file_stat; + time_t file_time = 0; + int retval = 0; + FILE *file; + + /* Make sure theres a file to sync with */ + if( !filename || !filename[0] ) + return( 0 ); + /* Get the file's time stamp so we know whether its new or old */ + if( !stat(filename, &file_stat) ) + { + file_time = file_stat.st_mtime; + } + else + { + /* Make an empty file if it doesn't exist */ + if( (file = fopen(filename, "w+")) ) + { + fclose(file); + } + } + if( (file = fopen(filename, "r")) ) + { + struct parse_state ps; + + /* Initialize the parser state */ + ps.ps_parent = parent; + ps.ps_filename = filename; + ps.ps_section_file = sf; + ps.ps_in_file = file; + ps.ps_out_file = 0; + ps.ps_section = 0; + ps.ps_line = 0; + ps.ps_column = 0; + ps.ps_data = 0; + ps.ps_len = 0; + if( sf->sf_time < file_time ) + { + /* The file is newer, read it in */ + if( (retval = parseFile(&ps)) ) + { + if( !parent ) + sf->sf_time = time(0); + } + } + else + { + /* The file is older, write out our data */ + if( (retval = writeFile(&ps)) ) + { + if( !parent ) + sf->sf_time = time(0); + } + } + fclose(file); + } + return( retval ); +} + +struct section_file *createSectionFile() +{ + struct section_file *retval; + + /* Allocate the structure and do any initialization */ + if( (retval = (struct section_file *) + KMALLOC(sizeof(struct section_file))) ) + { + int lpc; + + retval->lock = 0; + retval->sf_filename = 0; + retval->sf_time = 0; + retval->sf_ordered_sections = 0; + retval->sf_last_section = &retval->sf_ordered_sections; + for( lpc = 0; lpc < SECTION_FILE_HASH_SIZE; lpc++ ) + { + retval->sf_sections[lpc] = 0; + } + } + return( retval ); +} + +void deleteSectionFile(struct section_file *sf) +{ + if( sf ) + { + struct section_file_data *sfd, *sfd_next; + int lpc; + + /* + * Walk through the section and have their handlers delete the + * section data objects. + */ + for( lpc = 0; lpc < SECTION_FILE_HASH_SIZE; lpc++ ) + { + sfd = sf->sf_sections[lpc]; + while( sfd ) + { + sfd_next = sfd->sfd_next; + sfd->sfd_type->fs_handler(sfd->sfd_type, + sf, + SFM_DELETE, + sfd); + sfd = sfd_next; + } + } + KFREE(sf); + } +} + +void setSectionFileName(struct section_file *sf, char *name) +{ + sf->sf_filename = name; +} + +/* + * States for parsing a section line made up of an identifier followed by + * white space, and a data string + */ +enum { + SS_TAG, /* Getting the tag */ + SS_GUTTER, /* White space between tag and value */ + SS_VALUE, /* Value to the end of the line, minus ws, comments */ +}; + +int parseSectionLine(struct parse_state *ps, char **tag, char **value, + FILE *out_file) +{ + int state, tag_start = -1, tag_end = -1; + int value_start = -1, value_end = -1; + int retval = 1, curr, line_len; + char *line, *save; + + line = ps->ps_data; + line_len = ps->ps_len; + save = ps->ps_save; + + /* We start out looking for the tag */ + state = SS_TAG; + /* Walk through the line */ + for( curr = 0; curr < line_len; curr++ ) + { + switch(line[curr]) + { + case '#': + /* Its a comment */ + if( out_file ) + { + /* Write out value */ + fwrite(*value, 1, strlen(*value), out_file); + if( value_end > 0 ) + { + /* + * Reset the trampled character at the + * end of value + */ + line[value_end + 1] = save[1]; + /* Write out the comment */ + fwrite(&line[value_end + 1], + 1, + line_len - (value_end + 1), + out_file); + } + else + { + fwrite(&line[curr], 1, line_len - curr, + out_file); + } + } + /* Nothing left to parse, jump to the end */ + curr = line_len; + break; + case 0: /* Trampled white space */ + case ' ': + case '\t': + switch(state) + { + case SS_TAG: + if( tag_start != -1 ) + { + /* Found the gutter */ + if( out_file ) + { + /* Write the tag out */ + fwrite(*tag, + 1, + strlen(*tag), + out_file); + /* + * Write out the trampled char + */ + fwrite(&save[0], + 1, 1, out_file); + } + state = SS_GUTTER; + } + else if( out_file ) + { + /* Write out the space */ + fwrite(&line[curr], 1, 1, out_file); + } + break; + case SS_GUTTER: + if( out_file ) + { + /* Write out the space */ + fwrite(&line[curr], 1, 1, out_file); + } + break; + case SS_VALUE: + if( out_file ) + { + /* + * If the trampled char was a linefeed + * then we need to write the value and + * the linefeed. + */ + if( (line[curr] == 0) && + (curr == line_len - 1) ) + { + fwrite(*value, 1, + strlen(*value), + out_file); + fwrite("\n", 1, 1, out_file); + } + } + break; + default: + break; + } + break; + case '\n': + if( out_file ) + { + /* Write out value and the linefeed */ + fwrite(*value, 1, strlen(*value), out_file); + fwrite(&line[curr], 1, 1, out_file); + } + break; + default: + switch(state) + { + case SS_TAG: + if( tag_start == -1 ) + tag_start = curr; + tag_end = curr; + break; + case SS_GUTTER: + state = SS_VALUE; + /* Fallthrough */ + case SS_VALUE: + if( value_start == -1 ) + value_start = curr; + value_end = curr; + break; + } + break; + } + ps->ps_column++; + } + if( (tag_start != -1) && (tag_end != -1) ) + { + /* Save the char we're gonna trample */ + save[0] = line[tag_end + 1]; + line[tag_end + 1] = 0; + *tag = &line[tag_start]; + } + else + { + *tag = ""; /* better than NULL... */ + } + if( (value_start != -1) && (value_end != -1) ) + { + /* Save the char we're gonna trample */ + save[1] = line[value_end + 1]; + line[value_end + 1] = 0; + *value = &line[value_start]; + } + else + { + *value = ""; + } + /* Check for I/O error */ + if( out_file && ferror(out_file) ) + retval = 0; + return( retval ); +} + +/* + * Silly function to restore the characters that parseLine tramples + */ +static void restoreLine(struct parse_state *ps) +{ + int save_idx = 0, lpc; + + for( lpc = 0; lpc < ps->ps_len; lpc++ ) + { + /* Find any nulls and replace with saved chars */ + if( !ps->ps_data[lpc] ) + { + ps->ps_data[lpc] = ps->ps_save[save_idx]; + save_idx++; + } + } +} + +/* Strings that represent `true' */ +static char *trueStrings[] = { + "1", + "true", + "yes", + "y", + "t", + 0 +}; + +/* Strings that represent `false' */ +static char *falseStrings[] = { + "0", + "false", + "no", + "n", + "f", + 0 +}; +/* + * Note: The order of the values in trueStrings and falseStrings is important, + * and should be of consistent style. + */ + +unsigned long parseFlagString(char *value, + unsigned long flags, + unsigned long flag_bit) +{ + int retval = flags; + int lpc; + + for( lpc = 0; trueStrings[lpc]; lpc++ ) + { + /* If `value' is true, then set the bit */ + if( !strcasecmp(value, trueStrings[lpc]) ) + { + retval |= flag_bit; + break; + } + /* If `value' is false, then clear the bit */ + if( !strcasecmp(value, falseStrings[lpc]) ) + { + retval &= ~flag_bit; + break; + } + } + return( retval ); +} + +char *makeFlagString(unsigned long flags, unsigned long flag_bit, char *value) +{ + /* Default to the old value */ + char *retval = value; + + /* Check for a change from the old value */ + if( parseFlagString(value, flags, flag_bit) != flags ) + { + int lpc; + + /* Loop through all the strings */ + for( lpc = 0; trueStrings[lpc]; lpc++ ) + { + /* + * If this true matches, then return the corresponding + * false string + */ + if( !strcasecmp(value, trueStrings[lpc]) ) + { + retval = falseStrings[lpc]; + break; + } + /* + * If this false matches, then return the corresponding + * true string + */ + if( !strcasecmp(value, falseStrings[lpc]) ) + { + retval = trueStrings[lpc]; + break; + } + } + } + return( retval ); +} + +int syncSectionFile(struct section_file *sf) +{ + int iLockRoot, retval; + + lockMutex(sf); + retval = syncFile(0, sf, sf->sf_filename); + unlockMutex(sf); + return( retval ); +} + +void addSectionType(struct file_section *fs) +{ + int hash; + + hash = hashName(fs->fs_name, FILE_SECTION_TYPES_SIZE); + fs->fs_next = file_section_types[hash]; + file_section_types[hash] = fs; +} + +struct file_section *findSectionType(char *name) +{ + struct file_section *retval = 0, *fs; + int hash; + + hash = hashName(name, FILE_SECTION_TYPES_SIZE); + fs = file_section_types[hash]; + while( fs && !retval ) + { + if( !strcmp(name, fs->fs_name) ) + { + retval = fs; + } + fs = fs->fs_next; + } + return( retval ); +} + +void addSectionToFile(struct section_file *sf, struct section_file_data *sfd) +{ + int iLockRoot, hash; + + hash = hashName(sfd->sfd_name, SECTION_FILE_HASH_SIZE); + lockMutex(sf); + sfd->sfd_next = sf->sf_sections[hash]; + /* The section is new to this file so we set the dirty bit */ + sfd->sfd_flags |= SFDF_DIRTY; + sf->sf_sections[hash] = sfd; + *sf->sf_last_section = sfd; + sf->sf_last_section = &sfd->sfd_order; + sfd->sfd_order = 0; + unlockMutex(sf); +} + +void remSectionFromFile(struct section_file *sf, struct section_file_data *sfd) +{ + sfd->sfd_flags |= SFDF_REMOVED; +} + +struct section_file_data *findSectionInFile(struct section_file *sf, + struct file_section *type, + char *name) +{ + struct section_file_data *retval = 0, *sfd; + int iLockRoot, hash; + + lockMutex(sf); + hash = hashName(name, SECTION_FILE_HASH_SIZE); + sfd = sf->sf_sections[hash]; + while( sfd && !retval ) + { + if( (type == sfd->sfd_type) && + !strcmp(name, sfd->sfd_name) ) + retval = sfd; + sfd = sfd->sfd_next; + } + unlockMutex(sf); + return( retval ); +} + +int walkFileSections(struct section_file *sf, + int (*handler)(void *arg, + struct section_file *sf, + struct section_file_data *sfd), + void *arg) +{ + struct section_file_data *sfd; + int iLockRoot, retval = 1; + + if( !sf ) + return( 0 ); + lockMutex(sf); + /* Walk over the ordered links */ + sfd = sf->sf_ordered_sections; + while( sfd && retval ) + { + retval = handler(arg, sf, sfd); + sfd = sfd->sfd_order; + } + unlockMutex(sf); + return( retval ); +} + +struct section_file_data *createFileSection(char *section_type, + char *section_name, + ...) +{ + struct section_file_data *retval = 0; + struct file_section *fs; + char *new_name; + va_list args; + + va_start(args, section_name); + /* Get the section type and ask it to create a section data object */ + if( (fs = findSectionType(section_type)) && + (new_name = (char *)KMALLOC(strlen(section_name) + 1)) ) + { + strcpy(new_name, section_name); + if( !fs->fs_handler(fs, 0, SFM_CREATE, + &retval, new_name, args) ) + { + KFREE(new_name); + retval = 0; + } + } + va_end(args); + return( retval ); +} + +void deleteFileSection(struct section_file_data *sfd) +{ + if( sfd ) + { + KFREE(sfd->sfd_name); + /* Ask the handler to clean up the object */ + sfd->sfd_type->fs_handler(sfd->sfd_type, 0, SFM_DELETE, sfd); + } +} Index: kaffe/kaffe/xprof/sectionFile.h diff -u /dev/null kaffe/kaffe/xprof/sectionFile.h:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/sectionFile.h Thu May 4 17:08:25 2000 @@ -0,0 +1,211 @@ +/* + * sectionFile.h + * Routines for reading/writing a formatted text file + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#ifndef __sectionfile_h +#define __sectionfile_h + +/* + * The section file code reads and writes files that can be used for storing + * interesting information. The files themselves are just plain text thats + * formatted with a number of directives to drive the interpretation of the + * rest of the data. The current set of recognized directives are: + * + * %include "" - Include another file + * + * %begin - Begin a section with the given type and name + * - Specify a value for an attribute + * ... + * + * %end - End a section + * # - Starts a comment that goes until the end of the line + * + * The code tries to keep the file pristine so the user can edit it without + * things moving around and disappearing in future edits. + * + * The internals of the system works by having the program define a set of + * types and handlers that can translate between the text names and values in + * the file and the internal data structures. The section file takes care + * of the majority of the work by parsing the outer structure of the file + * as well as providing functions for handling tasks common to all handlers. + */ + +#include +#include + +#include "gtypes.h" +#include "locks.h" + +/* Identifiers for operations that file_section_handler's implement */ +enum { + SFM_CREATE, /* + * (struct section_file_data **out_sfd, + * char *name, + * va_list values) Create a new section_data structure + */ + SFM_DELETE, /* (struct section_file_data *sfd) Delete section */ + SFM_CACHE, /* + * (struct section_file_data *sfd, + * struct parse_state *ps, + * char *line, + * int len) Read the data from the file + */ + SFM_FLUSH, /* + * (struct section_file_data *sfd, + * struct parse_state *ps, + * char *line, int line_len, + * FILE *out_file) Write the data to the file + */ +}; + +/* Forward decls */ +struct section_file; +struct file_section; +struct parse_state; + +/* Typedef for a handler function for a section */ +typedef int (*file_section_handler_t)(struct file_section *, + struct section_file *, + int method, + ...); + +/* Structure specifying a type of section in a file */ +struct file_section { + struct file_section *fs_next; /* Hash table link */ + char *fs_name; /* Name of the section */ + file_section_handler_t fs_handler; /* Handler for this section */ +}; + +/* Bit numbers for section_file_data flags */ +enum { + SFDB_DIRTY, /* Theres new data in the section */ + SFDB_REMOVED, /* The section needs to be removed from the file */ +}; + +/* Flag bits for section_file_data flags */ +enum { + SFDF_DIRTY = (1L << SFDB_DIRTY), + SFDF_REMOVED = (1L << SFDB_REMOVED), +}; + +/* Stores data for a section */ +struct section_file_data { + struct section_file_data *sfd_next; /* Hash table link */ + struct section_file_data *sfd_order; /* Time ordered link */ + struct file_section *sfd_type; /* The type of this section */ + unsigned long sfd_flags; /* Flags for this section */ + char *sfd_name; /* Name of the section */ +}; + +/* The number of buckets for the section_file hash table */ +#define SECTION_FILE_HASH_SIZE 31 + +/* The root structure for a section file */ +struct section_file { + iLock *lock; + char *sf_filename; /* The name of the file */ + time_t sf_time; /* The time stamp for the last file sync */ + /* + * Time ordered link through the sections, the earliest added is the + * first in the list + */ + struct section_file_data *sf_ordered_sections; + /* Pointer to the last order pointer in the sf_ordered_sections list */ + struct section_file_data **sf_last_section; + /* The hash table of data sections */ + struct section_file_data *sf_sections[SECTION_FILE_HASH_SIZE]; +}; + +/* + * Create a section file structure + */ +struct section_file *createSectionFile(void); +/* + * Delete the section file structure + */ +void deleteSectionFile(struct section_file *sf); +/* + * Set the file name of the section file + */ +void setSectionFileName(struct section_file *sf, char *name); +/* + * Synchronize the section_file structure with the actual file. + */ +int syncSectionFile(struct section_file *sf); +/* + * Add a section type to the system + */ +void addSectionType(struct file_section *fs); +/* + * Find a section type + */ +struct file_section *findSectionType(char *name); +/* + * Add a section to a file + */ +void addSectionToFile(struct section_file *sf, struct section_file_data *sfd); +/* + * Schedule a section to be removed from the file + */ +void remSectionFromFile(struct section_file *sf, + struct section_file_data *sfd); +/* + * Find a section in a file + */ +struct section_file_data *findSectionInFile(struct section_file *sf, + struct file_section *type, + char *name); +/* + * Create a file section of the given type, with the given name + */ +struct section_file_data *createFileSection(char *section_type, + char *section_name, + ...); +/* + * Delete a file section data object + */ +void deleteFileSection(struct section_file_data *sfd); +/* + * Walk over the collection of sections + */ +int walkFileSections(struct section_file *sf, + int (*handler)(void *arg, + struct section_file *sf, + struct section_file_data *sfd), + void *arg); +/* + * Helper function for file_section handlers to parse a line into an identifier + * and value + */ +int parseSectionLine(struct parse_state *ps, char **tag, char **value, + FILE *out_file); +/* + * Helper function for file_section handlers to parse a flag string + * (e.g. "true", "false", etc...) and return the `flags' bitmask with the + * `flag_bit' set/cleared. + */ +unsigned long parseFlagString(char *value, + unsigned long flags, + unsigned long flag_bit); +/* + * Helper function for file_section handlers to get a string which that + * indicates whether `flag_bit' is set/cleared in `flags'. The `value' + * string should be the previous value of the flag, and the string + * returned tries mimic the value. For example, if value is "true", but + * the bit is cleared in the flags, the returned string is "false", instead + * of "0, "f", etc... + */ +char *makeFlagString(unsigned long flags, unsigned long flag_bit, char *value); + +#endif /* __sectionfile_h */ Index: kaffe/kaffe/xprof/xprofiler.c diff -u /dev/null kaffe/kaffe/xprof/xprofiler.c:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/xprofiler.c Thu May 4 17:08:26 2000 @@ -0,0 +1,688 @@ +/* + * xprofiler.c + * Interface functions to the profiling code + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#if defined(KAFFE_XPROFILER) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtypes.h" +#include "md.h" +#include "exception.h" +#include "jmalloc.h" +#include "methodCache.h" + +#include "memorySamples.h" +#include "feedback.h" +#include "debugFile.h" +#include "fileSections.h" +#include "gmonFile.h" +#include "callGraph.h" +#include "xprofiler.h" + +/* Variables for figuring out the start and end of the program text */ +extern char _start; +extern char etext; + +int xProfFlag = 0; +/* Flag used to determine whether or not we should record `hits' */ +static volatile int xProfRecord = 1; + +/* + * This variable is part of a nasty hack to keep the call graph accounting + * code from being credited with time. The accounting function should set + * this variable while its working so that if a profiling interrupt happens + * while its executing the appropriate function will be credited. + */ +static char * volatile profiler_sample_override_pc = 0; +volatile int profiler_sample_overrides = 0; + +/* Structures used to hold the profiling information */ +struct memory_samples *kaffe_memory_samples = 0; +struct call_graph *kaffe_call_graph = 0; + +/* The name of the output files */ +char *kaffe_gmon_filename = "xgmon.out"; +char *kaffe_syms_filename = "kaffe-jit-symbols.s"; + +/* Debugging file for profiler symbols */ +struct debug_file *profiler_debug_file = 0; + +/* Number of call_arc structures to preallocate */ +#define XPROFILE_ARCS (1024 * 64) + +/* + * The gutter threshold is used to determine if the gap between two chunks of + * observed memory is too large to put into the file. Since the resulting gap + * would just be a bunch of zeros, we split them into different files. + */ +#define SAMPLE_GUTTER_THRESHOLD 1024 * 1024 * 5 + +/* Structure used to track the current gmon file for our walker */ +struct profiler_gmon_file { + char *pgf_stage; /* The name of the current stage */ + struct gmon_file *pgf_file; /* The active gmon file structure */ + long pgf_record; /* The index of the hist record */ +}; + +/* + * A walker function for walkMemorySamples. This does a little more than + * the one provided by the gmon code since it will create a new gmon file + * if theres a large gap in observed memory. + */ +static int profilerSampleWalker(void *handle, char *addr, + short *bins, int size) +{ + struct profiler_gmon_file *pgf = handle; + struct gmon_file *gf = pgf->pgf_file; + int retval = 0; + + /* Check if we need to dump these samples in another file */ + if( (addr - gf->gf_addr) > SAMPLE_GUTTER_THRESHOLD ) + { + char *filename, *old_high; + int len; + + old_high = gf->gf_high; + /* Rewrite the old record with the new high address */ + writeGmonRecord(gf, + GRA_Rewrite, pgf->pgf_record, + GRA_Type, GMON_TAG_TIME_HIST, + GRA_LowPC, gf->gf_low, + GRA_HighPC, gf->gf_addr, + GRA_DONE); + writeCallGraph(kaffe_call_graph, gf); + /* Close down the file */ + deleteGmonFile(gf); + pgf->pgf_file = 0; + gf = 0; + + /* + * Make up the filename for the new file, we'll just use the + * starting address for this range to make it unique + */ + len = strlen(kaffe_gmon_filename) + + 1 + /* `.' */ + 2 + (sizeof(void *) * 2) + /* `0x...' */ + (pgf->pgf_stage ? strlen(pgf->pgf_stage) + 1 : 0) + + 1; + if( (filename = (char *)KMALLOC(len)) ) + { + /* Construct the new file name */ + sprintf(filename, + "%s.%p%s%s", + kaffe_gmon_filename, + addr, + pgf->pgf_stage ? "." : "", + pgf->pgf_stage ? pgf->pgf_stage : ""); + if( (pgf->pgf_file = createGmonFile(filename)) ) + { + gf = pgf->pgf_file; + /* Write out another hist record */ + pgf->pgf_record = writeGmonRecord( + gf, + GRA_Type, GMON_TAG_TIME_HIST, + GRA_LowPC, addr, + GRA_HighPC, old_high, + GRA_DONE); + } + } + } + if( gf ) + { + /* Let the gmon walker do its thing */ + retval = gmonSampleWalker(gf, addr, bins, size); + } + memset(bins, 0, size * sizeof(short)); + return( retval ); +} + +/* + * Handles any post processing + */ +static void profilerAtExit(void) +{ + if( !xProfFlag ) + return; + /* We don't care about profiling anymore */ + disableProfileTimer(); + /* This is the final stage, write out any data */ + xProfileStage(0); + /* Release everything else */ + disableXProfiling(); +} + +int enableXCallGraph(void) +{ + int retval = false; + + if( (kaffe_call_graph = createCallGraph(XPROFILE_ARCS)) ) + { + retval = true; + } + return( retval ); +} + +#if defined(KAFFE_CPROFILER) +/* + * Return the minimum cycles per second for the machine + */ +static int hertz(void) +{ + struct itimerval value, ovalue; + int retval = 0; + + timerclear(&value.it_interval); + timerclear(&value.it_value); + timerclear(&ovalue.it_interval); + timerclear(&ovalue.it_value); + value.it_interval.tv_usec = 1; + setitimer(ITIMER_REAL, &value, 0); + getitimer(ITIMER_REAL, &ovalue); + timerclear(&value.it_interval); + timerclear(&value.it_value); + setitimer(ITIMER_REAL, &value, 0); + if( ovalue.it_interval.tv_usec >= 2 ) + retval = 1000000 / ovalue.it_interval.tv_usec; + return( retval ); +} + +#if defined(KAFFE_STD_PROF_RATE) +KAFFE_STD_PROF_RATE +#endif +#endif + +int enableXProfiling(void) +{ + int retval = false; + + xProfilingOff(); + /* Start up our profiler and set it to observe the main executable */ + if( xProfFlag && + enableProfileTimer() && + (kaffe_memory_samples = createMemorySamples()) && + observeMemory(kaffe_memory_samples, &_start, &etext - &_start) && + (profiler_debug_file = createDebugFile(kaffe_syms_filename)) && + !atexit(profilerAtExit) ) + { +#if defined(KAFFE_CPROFILER) + struct gmonparam *gp = &_gmonparam; + int prof_rate; +#endif + +#if defined(KAFFE_CPROFILER) + /* Turn off any other profiling */ + profil(0, 0, 0, 0); +#if defined(KAFFE_STD_PROF_RATE) + if( !(prof_rate = kaffeStdProfRate()) ) +#endif + prof_rate = hertz(); /* Just guess */ + if( !prof_rate ) + prof_rate = 100; + /* Copy the hits leading up to now into our own counters */ + if( gp->kcountsize > 0 ) + { + int lpc, len = gp->kcountsize / sizeof(HISTCOUNTER); + HISTCOUNTER *hist = gp->kcount; + int scale; + + scale = (gp->highpc - gp->lowpc) / len; + for( lpc = 0; lpc < len; lpc++ ) + { + if( hist[lpc] ) + { + char *pc = ((char *)gp->lowpc) + + (lpc * scale); + + memoryHitCount(kaffe_memory_samples, + pc, + (100 * hist[lpc]) / + prof_rate); + } + } + } +#endif + retval = true; + } + else + { + xProfFlag = 0; + disableXProfiling(); + } + xProfilingOn(); + return( retval ); +} + +void disableXProfiling(void) +{ + /* Shutoff the timer and delete any structures */ + disableProfileTimer(); + xProfilingOff(); + deleteMemorySamples(kaffe_memory_samples); + kaffe_memory_samples = 0; + deleteCallGraph(kaffe_call_graph); + kaffe_call_graph = 0; + deleteDebugFile(profiler_debug_file); + profiler_debug_file = 0; +} + +void xProfilingOn(void) +{ +#if defined(KAFFE_CPROFILER) + struct gmonparam *gp = &_gmonparam; + + gp->state = GMON_PROF_ON; +#endif + xProfRecord = 1; +} + +void xProfilingOff(void) +{ +#if defined(KAFFE_CPROFILER) + struct gmonparam *gp = &_gmonparam; + + gp->state = GMON_PROF_OFF; +#endif + xProfRecord = 0; +} + +void xProfileStage(char *stage_name) +{ + char *low, *high, *filename; + struct gmon_file *gf; + short misses; + int len; + + if( !xProfFlag ) + return; + xProfilingOff(); + low = kaffe_memory_samples->ms_low; + high = kaffe_memory_samples->ms_high + 2; + misses = kaffe_memory_samples->ms_misses; + /* Dump some helpful symbols */ + addDebugInfo(profiler_debug_file, + DIA_FunctionSymbolS, "_unaccounted_", high - 2, -1, + DIA_FunctionSymbolS, "_jit_end_", high, -1, + DIA_DONE); + xProfilingOff(); /* addDebugInfo turns it back on */ + len = strlen(kaffe_gmon_filename) + + (stage_name ? strlen(stage_name) + 1 : 0) + + 1; + if( (filename = (char *)KMALLOC(len)) ) + { + sprintf(filename, + "%s%s%s", + kaffe_gmon_filename, + stage_name ? "." : "", + stage_name ? stage_name : ""); + /* Create and write out the gmon file */ + if( (gf = createGmonFile(filename)) ) + { + struct profiler_gmon_file pgf; + + /* Initialize the profiler_gmon_files state */ + pgf.pgf_stage = stage_name; + pgf.pgf_file = gf; + /* Write out the samples */ + pgf.pgf_record = + writeGmonRecord(gf, + GRA_Type, GMON_TAG_TIME_HIST, + GRA_LowPC, low, + GRA_HighPC, high, + GRA_DONE); + walkMemorySamples(kaffe_memory_samples, + low, + &pgf, + profilerSampleWalker); + if( pgf.pgf_file ) + { + /* + * Record the number of hits that were outside + * of observed memory + */ + writeGmonSamples(pgf.pgf_file, + high - 2, &misses, 1); + /* Dump out the call graph data */ + writeCallGraph(kaffe_call_graph, pgf.pgf_file); + /* Record the number of arcs we missed */ + writeGmonRecord(pgf.pgf_file, + GRA_Type, GMON_TAG_CG_ARC, + GRA_FromPC, high - 2, + GRA_SelfPC, high - 2, + GRA_Count, kaffe_call_graph-> + cg_misses, + GRA_DONE); + + deleteGmonFile(pgf.pgf_file); + } + } + } + resetCallGraph(kaffe_call_graph); + xProfilingOn(); +} + +int profileGmonFile(char *name) +{ + /* Just set the file for now, we'll make it at exit */ + kaffe_gmon_filename = name; + return( true ); +} + +int profileSymbolFile(char *name) +{ + kaffe_syms_filename = name; + return( true ); +} + +#if defined(KAFFE_XPROFILER) +static void profileTimerHandler(SIGNAL_ARGS(sig, sc)) +{ + SIGNAL_CONTEXT_POINTER(scp) = GET_SIGNAL_CONTEXT_POINTER(sc); + char *addr = (char *)SIGNAL_PC(scp); + + if( kaffe_memory_samples && xProfRecord ) + { + if( profiler_sample_override_pc ) + { + profiler_sample_overrides++; + /* Use the override address */ + addr = profiler_sample_override_pc; + /* Null it out to stop false positives */ + profiler_sample_override_pc = 0; + } + memoryHit(kaffe_memory_samples, addr); + } +} + +int enableProfileTimer(void) +{ + struct sigaction sa; + int retval = false; + + /* Setup our signal handler */ + sa.sa_handler = (SIG_T)profileTimerHandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = 0; + if( sigaction(SIGALRM, &sa, 0) >= 0 ) + { + struct itimerval new_value; + + /* Setup a 10ms timer */ + new_value.it_interval.tv_sec = 0; + new_value.it_interval.tv_usec = 10000; + new_value.it_value.tv_sec = 0; + new_value.it_value.tv_usec = 10000; + if( setitimer(ITIMER_REAL, &new_value, 0) >= 0 ) + { + retval = true; + } + } + return( retval ); +} + +void disableProfileTimer(void) +{ + struct itimerval disable_value; + + timerclear(&disable_value.it_interval); + timerclear(&disable_value.it_value); + setitimer(ITIMER_REAL, &disable_value, 0); +} +#else +int enableProfileTimer(void) +{ + return( 1 ); +} + +void disableProfileTimer(void) +{ +} +#endif + +/* The rest of these are just wrappers for other code */ + +int profileFunction(struct mangled_method *mm, char *code, int codelen) +{ + int retval = false; + + if( xProfFlag && kaffe_memory_samples ) + { + /* + * Add the function name to the symbol file and observe the + * memory + */ + xProfilingOff(); + if( observeMemory(kaffe_memory_samples, code, codelen) && + addDebugInfo(profiler_debug_file, + DIA_FunctionSymbol, mm, code, codelen, + DIA_DONE) ) + { + retval = true; + } + xProfilingOn(); + } + else + retval = true; + return( retval ); +} + +int profileMemory(char *code, int codelen) +{ + int retval = false; + + if( xProfFlag ) + { + xProfilingOff(); + if( observeMemory(kaffe_memory_samples, code, codelen) ) + { + retval = true; + } + xProfilingOn(); + } + else + retval = true; + return( retval ); +} + +int profileSymbol(struct mangled_method *mm, char *addr, int size) +{ + int retval = false; + + if( xProfFlag ) + { + if( addDebugInfo(profiler_debug_file, + DIA_FunctionSymbol, mm, addr, size, + DIA_DONE) ) + { + retval = true; + } + } + else + retval = true; + return( retval ); +} + +void profileArcHit(char *frompc, char *selfpc) +{ + char *old_override = profiler_sample_override_pc; + + /* + * profiler_sample_override_pc is a nasty hack to keep the + * accounting function from being counted as a hit when it + * should really be the caller. + */ + profiler_sample_override_pc = selfpc; + if( kaffe_call_graph && xProfRecord ) + { + arcHit(kaffe_call_graph, frompc, selfpc); + } + profiler_sample_override_pc = old_override; +} + +#if defined(KAFFE_XPROFILER) && defined(KAFFE_CPROFILER) && defined(_KAFFE_OVERRIDE_MCOUNT_DEF) +_KAFFE_OVERRIDE_MCOUNT_DEF +{ + char *old_override = profiler_sample_override_pc; + + /* + * profiler_sample_override_pc is a nasty hack to keep the + * accounting function from being counted as a hit when it + * should really be the caller. + */ + profiler_sample_override_pc = (char *)selfpc; + if( kaffe_call_graph ) + { + register struct gmonparam *gp = &_gmonparam; + + if( !xProfRecord || (gp->state != GMON_PROF_ON) ) + goto fini; + arcHit(kaffe_call_graph, (char *)frompc, (char *)selfpc); + } + else + { + register u_short *frompcindex; + register struct tostruct *top, *prevtop; + register struct gmonparam *p; + register long toindex; + + p = &_gmonparam; + /* + * check that we are profiling + * and that we aren't recursively invoked. + */ + if( p->state != GMON_PROF_ON ) + goto fini; + p->state = GMON_PROF_BUSY; + + /* + * check that frompcindex is a reasonable pc value. + * for example: signal catchers get called from the stack, + * not from text space. too bad. + */ + frompc -= p->lowpc; + if (frompc > p->textsize) + goto done; + + frompcindex = &p->froms[frompc / + (p->hashfraction * sizeof(*p->froms))]; + toindex = *frompcindex; + if (toindex == 0) { + /* + * first time traversing this arc + */ + toindex = ++p->tos[0].link; + if (toindex >= p->tolimit) + /* halt further profiling */ + goto overflow; + + *frompcindex = toindex; + top = &p->tos[toindex]; + top->selfpc = selfpc; + top->count = 1; + top->link = 0; + goto done; + } + top = &p->tos[toindex]; + if (top->selfpc == selfpc) { + /* + * arc at front of chain; usual case. + */ + top->count++; + goto done; + } + /* + * have to go looking down chain for it. + * top points to what we are looking at, + * prevtop points to previous top. + * we know it is not at the head of the chain. + */ + for (; /* goto done */; ) { + if (top->link == 0) { + /* + * top is end of the chain and none of the + * chain had top->selfpc == selfpc. so we + * allocate a new tostruct and link it to the + * head of the chain. + */ + toindex = ++p->tos[0].link; + if (toindex >= p->tolimit) + goto overflow; + + top = &p->tos[toindex]; + top->selfpc = selfpc; + top->count = 1; + top->link = *frompcindex; + *frompcindex = toindex; + goto done; + } + /* + * otherwise, check the next arc on the chain. + */ + prevtop = top; + top = &p->tos[top->link]; + if (top->selfpc == selfpc) { + /* + * there it is. + * increment its count + * move it to the head of the chain. + */ + top->count++; + toindex = prevtop->link; + prevtop->link = top->link; + top->link = *frompcindex; + *frompcindex = toindex; + goto done; + } + + } + done: + p->state = GMON_PROF_ON; + goto fini; + overflow: + p->state = GMON_PROF_ERROR; + goto fini; + } + fini: + profiler_sample_override_pc = old_override; +} + +_KAFFE_OVERRIDE_MCOUNT +#endif + +void profileHit(char *addr) +{ + if( kaffe_memory_samples && xProfRecord ) + { + if( profiler_sample_override_pc ) + { + profiler_sample_overrides++; + /* Use the override address */ + addr = profiler_sample_override_pc; + /* Null it out to stop false positives */ + profiler_sample_override_pc = 0; + } + memoryHit(kaffe_memory_samples, addr); + } +} + +#endif /* KAFFE_XPROFILER */ Index: kaffe/kaffe/xprof/xprofiler.h diff -u /dev/null kaffe/kaffe/xprof/xprofiler.h:1.1 --- /dev/null Mon May 22 09:48:45 2000 +++ kaffe/kaffe/xprof/xprofiler.h Thu May 4 17:08:28 2000 @@ -0,0 +1,108 @@ +/* + * xprofiler.h + * Interface functions to the cross language profiling code + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#ifndef __xprofiler_h +#define __xprofiler_h + +#include "mangle.h" + +/* + * Enable profiling + */ +int enableXProfiling(void); +/* + * Enable call graph profiling, this is separate from enableXProfiling since + * it can safely be started before the VM. + */ +int enableXCallGraph(void); +/* + * Disable profiling and deallocate any accounting structures + */ +void disableXProfiling(void); +/* + * Turn on cross language profiling, this causes hits to be recorded, otherwise + * they are ignored. (The default is to record hits). + */ +#if defined(KAFFE_XPROFILER) +void xProfilingOn(void); +#else +#define xProfilingOn() +#endif +/* + * Turn off cross language profiling, stop recording any hits. + */ +#if defined(KAFFE_XPROFILER) +void xProfilingOff(void); +#else +#define xProfilingOff() +#endif +/* + * Signal the end of a stage, this will write out gmon files containing the + * current profiling values and then reset them to zero. + * + * stage_name - This should be the name of the just completed stage, it is then + * appended to the gmon file name to distinguish from the others. + */ +void xProfileStage(char *stage_name); +/* + * Set the base name of the generated gmon file(s), defaults to "gmon.out" + */ +int profileGmonFile(char *name); +/* + * Set the name of the generated assembler file with the profiler symbols. + * Returns success if the file was created, or a previous call was also + * successful. + */ +int profileSymbolFile(char *name); +/* + * Increment the sample count for the given address + */ +void profileHit(char *addr); +/* + * Increment the count for the given call arc + */ +void profileArcHit(char *frompc, char *selfpc); +/* + * Start to record hits for the memory covered by this function, and add the + * functions symbol/address to the symbol file + */ +int profileFunction(struct mangled_method *mm, char *code, int codelen); +/* + * Start to record hits for the specified segment of memory + */ +int profileMemory(char *code, int codelen); +/* + * Add the given symbol to the profile symbol file. (Note: use -1 for unknown + * size) + */ +int profileSymbol(struct mangled_method *mm, char *addr, int size); +/* + * Enable the profile timer code + */ +int enableProfileTimer(void); +/* + * Disable the profile timer code + */ +void disableProfileTimer(void); + +/* + * Indicates whether cross language monitoring code should be added/executed. + * If this is on, but xProfilingOff() has been called then the appropriate + * code is still added/executed, but any hits generated by this code isn't + * recorded. + */ +extern int xProfFlag; + +#endif /* __xprofiler_h */ Index: kaffe/libraries/clib/management/XProfiler.c diff -u /dev/null kaffe/libraries/clib/management/XProfiler.c:1.2 --- /dev/null Mon May 22 09:48:46 2000 +++ kaffe/libraries/clib/management/XProfiler.c Fri May 5 09:30:56 2000 @@ -0,0 +1,46 @@ +/* + * XProfiler.c + * Java wrappers for the xprofiler functions + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ + +#include "config.h" +#include "../../../kaffe/kaffevm/gtypes.h" +#include "../../../kaffe/xprof/xprofiler.h" +#include "kaffe_management_XProfiler.h" +#include "../../../kaffe/kaffevm/stringSupport.h" +#include + +void Java_kaffe_management_XProfiler_on(JNIEnv *env, jclass clazz) +{ +#if defined(KAFFE_XPROFILER) + xProfilingOn(); +#endif +} + +void Java_kaffe_management_XProfiler_off(JNIEnv *env, jclass clazz) +{ +#if defined(KAFFE_XPROFILER) + xProfilingOff(); +#endif +} + +void Java_kaffe_management_XProfiler_stage(JNIEnv *env, jclass clazz, + jstring _stage_name) +{ +#if defined(KAFFE_XPROFILER) + char *stage_name = stringJava2C(_stage_name); + + xProfileStage(stage_name); + KFREE(stage_name); +#endif +} Index: kaffe/libraries/javalib/Makefile.am diff -u kaffe/libraries/javalib/Makefile.am:1.1.1.1 kaffe/libraries/javalib/Makefile.am:1.2 --- kaffe/libraries/javalib/Makefile.am:1.1.1.1 Thu May 4 16:13:23 2000 +++ kaffe/libraries/javalib/Makefile.am Thu May 4 17:02:08 2000 @@ -688,7 +688,8 @@ kaffe_management_SRCS = \ kaffe/management/Classpath.java \ kaffe/management/Debug.java \ - kaffe/management/JIT.java + kaffe/management/JIT.java \ + kaffe/management/XProfiler.java kaffe_net_SRCS = \ kaffe/net/DefaultDatagramSocketImplFactory.java \ kaffe/net/DefaultFileNameMap.java \ Index: kaffe/libraries/javalib/kaffe/management/XProfiler.java diff -u /dev/null kaffe/libraries/javalib/kaffe/management/XProfiler.java:1.2 --- /dev/null Mon May 22 09:48:56 2000 +++ kaffe/libraries/javalib/kaffe/management/XProfiler.java Fri May 5 09:30:58 2000 @@ -0,0 +1,43 @@ +/* + * XProfiler.java + * Java interface to the cross language profiler + * + * Copyright (c) 2000 University of Utah and the Flux Group. + * All rights reserved. + * + * This file is licensed under the terms of the GNU Public License. + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * Contributed by the Flux Research Group, Department of Computer Science, + * University of Utah, http://www.cs.utah.edu/flux/ + */ +package kaffe.management; + +/** + * This class provides access to the underlying cross language profiling + * infrastructure. Currently, its limited to turning accounting on or off + * and starting a new profiling stage. + */ +public class XProfiler { + + static { + System.loadLibrary("management"); + } + + /** + * Turn profile accounting on, the default + */ + public static native void on(); + /** + * Turn profile accounting off + */ + public static native void off(); + + /** + * Transition to a new profiling stage, the given name is used to + * identify the previous stage. + */ + public static native void stage(String name); + +}