From: Jens W. <jen...@de...> - 2007-05-11 17:48:20
|
Hi, =20 This is (another) try to address the profiling of jitted code generated by = (J)VMs.=20 I tried to incorporate all the previous ideas posted on this list, especial= ly the=20 work from Jason Yeh. The new implementation tries to be modular and extensi= ble=20 and should be easy to integrate. The concept of libopagent is kept, but the= ELF=20 files are generated for a complete vma region at the end of the profile run. The patches are against Oprofile-0.9.2, however the existing code is only t= ouched very lightly, so it should be easy to apply it to CVS HEAD or other version= s. I tested the code on a x86 32 bit system, but it should work on other archi= tectures=20 also. Please give feedback. Usage instructions for Java / JVMTI can be found in the README of the JVMTI= agent patch. The following text describes the design ideas. Comments, especially on the "(Open) issues", below are welcome. =3D Design Goals =3D =2D The interface of profiling agents should be generic and work=20 for various JVMs or JIT compilers just as Mono [JY] The following text discusses the complete scenario for Java and a JVM with the JVMT Interface. =2D The mechanism must require only minimum changes to the current=20 Oprofile infrastructure [JY] =2D Overhead during sampling should be low =2D The code should be platform agnostic =3D Terms =3D JVMTI: Java Virtual Machine Tool Interface. Standard interface for tools to instru= ment=20 a JVM. See [JVMTI] agent: defined in the JVMTI specification. The agent is a client which instruments= a=20 JVM through using JVMTI. Current state =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D This section describes the state as it is in the Oprofile 0.9.2 release. An opreport -l output of a SPECJbb2000 run on IBM jre 1.5 SR4 on S390x=20 gives the following result: =2D--snip--- samples % samples % image name app name = symbol name 20029 50.4331 28004 60.6541 anon (tgid:1720 range:0x20022b73000-0x2= 0023373000) java (no symbols) 8699 21.9041 654 1.4165 vmlinux-2.6.19-21.x.20070130-s390xdefau= lt vmlinux-2.6.19-21.x.20070130-s390xdefault cpu_idle 6035 15.1962 10727 23.2337 anon (tgid:1720 range:0x200212fb000-0x2= 0021afb000) java (no symbols) 1920 4.8346 2721 5.8934 libj9gc23.so java = J9ZeroMemory 314 0.7907 260 0.5631 libj9gc23.so java = MM_MarkingScheme::markObjectNoCheck(MM_Environment*, J9Object*, = bool) 253 0.6371 224 0.4852 libj9gc23.so java = MM_MarkingScheme::scanObject(MM_Environment*, J9Object*) 201 0.5061 239 0.5177 libj9gc23.so java = MM_MarkingScheme::scanMixedObject(MM_Environment*, J9Object*) 183 0.4608 282 0.6108 anon (tgid:1720 range:0x200002ba000-0x2= 00002c6000) java (no symbols) 119 0.2996 152 0.3292 libj9gc23.so java = MM_MarkingScheme::markObject(MM_Environment*, J9Object*, bool) 101 0.2543 173 0.3747 libj9prt23.so java = j9time_current_time_millis =2D--snip--- This means that for this example workload more than 80% of CPU time is spen= t=20 in dynamically generated code.=20 =46or this code no evaluation on symbol/method level is possible, yet. =3D Profiling data structures =3D During profiling the data is gathered by the Linux kernel and further proce= ssed=20 in user space by the Oprofile daemon process. The daemon stores the sample = results=20 in the directory: /var/lib/oprofile/samples/current. We will discuss the structure of the data in this directory by example. In = a test=20 run (with option -seperate=3Dall) the following files were created (excerpt= ): =2E/{root}/bin/bash/{dep}/{root}/lib64/libc-2.4.90.so/TIMER.0.0.1717.1717.0 =2E/{root}/bin/bash/{dep}/{root}/lib64/libc-2.4.90.so/TIMER.0.0.1738.1738.1 =2E/{root}/bin/bash/{dep}/{root}/bin/bash/TIMER.0.0.1738.1738.1 The file itself contains the sample events of the profiling run. In the dir= ectory=20 information the binary is encoded that was executed. The first part of the subdirectory names is {root}/bin/bash, this is the bi= nary that=20 was run. The second directory part is the dependent library. E.g.=20 {dep}/{root}/lib64/libc-2.4.90.so is the library or binary that was mapped = in. All=20 mapped binaries are modeled as dependent binary, even the executable itself= =2E=20 The filename has the following fields: event name, event count, unit mask,= =20 task group ID, task ID, CPU number. The collected samples for each binary,= =20 or also task group ID or CPU (depending on the =E2=80=93separate option) wi= ll be put=20 in an individual file. For a more detailed explanation see [OPI]. As of Version 0.9.2 Oprofile has support for storing profiling data of=20 dynamically generated code. This is done by storing the memory range of=20 the code region as an anonymous dependency. E.g.: ./{root}/opt/ibm-java2-s390x-50/jre/bin/java/{dep}/ {anon}/1720.0x200002ba000.0x200002c6000/TIMER.0.0.1720.1735.0 The generated directory name for the anonymous code region has the fields:= =20 task group ID, start address and end address. Design outline =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D Gathering data from the JVM =3D=20 During JVM runtime a so called agent connects via JVMTI (or JVMPI). The age= nt gets=20 notified whenever a new method is dynamically compiled by the just-in-time= =20 compiler (see [JVMTI]). The agent implementation is a dynamic library.=20 The events from the JVM are invocations of C callback functions. On behalf of the event the method binary code, symbol name and starting add= ress=20 will be written sequencially to a file. The agent implementation does not=20 write directly to that file, instead, we use an abstraction library (libopa= gent)=20 for this purpose This means the JVMTI agent will be linked against libopage= nt. The written file will be further referred to as the JIT-dump file. There may be more than one JVM running during a profile session, each gener= ates its own JIT-dump file. No interaction to the Oprofile daemon takes places. =3D Generation of ELF files =3D The JIT-dump files are just intermediate files and cannot processed by the= =20 Oprofile analysis tools. To have a easily processable and portable file=20 the code and symbol information is converted to an ELF file. Oprofile or its control program opcontrol has the command dump, which flush= es=20 the sample buffers and the command stop, which stops the sampling (data gat= hering) and also does a dump internally to flush the sample buffers. The rationale behind the dump command is (thats a guess maybe someone can d= etail=20 on this?) to have already valid data in the filesystem to run the analysis = tools=20 already during the profiling session. With the enhancement we want to support dump and stop correctly, meaning th= at the=20 ELF files will be generated when a dump command is issued with the data tha= t is=20 currently available. On stop the ELF files will also be generated. If there= are=20 existing files from a previous dump these files will be overwritten. The generation of the ELF files works this way: =2D Find stored sample data for anonymous region =2D Check whether there is a JIT-Dump for this process =2D Generate an ELF file for this region containing the matching symbols an= d code from the JIT-Dump file The ELF filename will be analog to the section name. The ELF file generation is done by the new program opjit2elf which is calle= d=20 for each reagion by the opcontrol shell script. =46or each profiling session separate ELF files are created, even if the sa= me=20 workload process is profiled. The generated ELF files are associated with=20 the sample data. If opcontrol =E2=80=93save or opcontrol =E2=80=93archive i= s used the ELF=20 files will also be put in the save directory or in the archive. =3D Reports =3D The report routines of Oprofile need no change, except that a filename for = each anonymous region is generated that refers to the proper ELF file. Library libopagent =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D The library is a generic interface for virtual machines that generate dynam= ic code=20 and to report that code and symbol information to Oprofile. =3D Interface =3D int op_open_agent(void); Open an agent session. void op_close_agent(void); Close an agent session and free resources. int op_write_native_code(char const * symbol_name, const void* code_addr,=20 unsigned int size); Write out symbol mapping and binary code. =3D Function =3D Before usage a VM must call the op_open_agent function. The file <pid>.dump= =20 will be created. Each call to op_write_native_code writes out the informati= on to=20 this file. The target directory must be world-writable because libopagent runs within = the JVM=20 user context.=20 The library writes the JIT-Dump files to the directory /var/lib/oprofile/ji= tdump. JIT-Dump file format =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D The file format of the JIT-Dump file is internal to Oprofile. Direct genera= tion of this file may be possible, but should not be done, because the format is= not=20 intended as stable interface. The format is designed to be compact and easily processable by C programs. =3D Header =3D The file has an ASCII header, so the header is readable and extensible. It is read line by line. Each line has the format "<Param>: <Value>". The end of the header is marked by an empty line (just as RFC822). Here is an example: =2D--snip--- Oprofile Agent JIT-Dump Version: 1 BFD-Target: elf32-i386 BFD-Arch: 8 BFD-Mach: 1 BFD-Printable-Name: i386 =2D--snip--- The sole purpose of the header information, right now, is to transport the= =20 machine archtecture of the code that the file contains. This is important=20 on 64 bit machines if we run 32/64 bit mixed VMs. =3D Records =3D After the header the file contains records of the following format: <type> <name> <description> char record_id ID of the record (always 0) uint32 record_size total size of this record, not including record_id and r= ecord_size fields char[] symbol_name symbol name of the code as 0 terminated string uint64 code_addr start address in memory of the code uint32 code_size size of the code char[] code the code itself The idea of the record_id field is to be extensible. Most likely we may want to transport additional information (see issues). The record_size is used t= o check whether the record is complete before it is parsed. It may happen that the = JIT-Dump file still gets written to, while we generate the ELF file. (Open) issues =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Here is a set of issues of potential problems and extension ideas. =3D Image / Library name =3D Oprofile shows in the reports the image name in a separate column.=20 Showing the region data (PID, start- and end-address) as it is now, is not = useful=20 for the user in the general case. Whats useful for a Java method? The defining class? The JAR-file? The data from java.lang.Package.getImplementationVendor() or=20 java.lang.Package.getImplementationTitle() would be useful candidates. To show customized output in the image name column we need to do changes to= =20 the analysis tools and find a way to transport the information. =3D Duplicate method names =3D It may happen that the method name (including the class) is jitted more than once, in the following cases: Multiple class loader, recompile, different signatures. But adding the signature and classloader everytime to a method name leads t= o=20 very long symbol names. An idea would be to add functionality, so that an optional prefix or suffix can be set that is only used in the event of duplicated names.=20 =3D libopagent packaging =3D libopagent needs to be packaged several times in a multi-lib distribution.= =20 E.g. to support 64 bit JVMs and 32 bit JVMs there need to be two libraries= =20 provided. =3D Missing anonymous samples =3D When processing the sample data the Oprofile daemon gets additional informa= tion for each process from /proc/<pid>/maps. There may be a delay between the sa= mple event and the processing, because of the fact that the sample data gets buf= fered. Short running processes may be already terminated when the Oprofile daemon = tries to access this data. In this case the anonymous samples will not be stored. A temporary workaround is to raise the buffer-watershed if short running programs get profiled. =3D Inlined methods =3D According to the JVMTI spec inlined methods for the =E2=80=9ECompiled Metho= d Load=E2=80=9C event=20 will be sent for each method with the identical address range. Resolved issues =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Here is a list of issues that are already resolved. The purpose of this sec= tion is to outline the design decisions taken. =3D Removal of JIT-dump files =3D When should the JIT-dump files be removed? =3D> The jitdump directory is cleared on opcontrol --reset. On opcontrol --start= =20 JIT-Dump files get removed, that are not open. =3D ELF filenames =3D Naming and position of the generated ELF files must be carefully considered= =2E=20 Impact on Oprofile add-on tools should be minimized. =3D> John Levon proposes to put the ELF files to /var/lib/oprofile/samples/curre= nt/{anon}/,=20 see [JL0502]=20 The naming is identical to the directory following {anon},=20 e.g. 1720.0x200002ba000.0x200002c6000. =3D Symbol name disambiguation =3D We need an unique symbol name to store the code in the ELF file. Java metho= d names=20 may not be unique, e.g. when a method is reloaded The question is: Do we r= equire an user of libopagent to report only unique symbol names, or do we ensure uniq= ueness=20 internally? =3D> We ensure uniquness internally by adding an index suffix, but maybe it make= s sense=20 to also have additional data that can be used for a more informative disamb= iguation,=20 see "Duplicate method names" above. =3D UTF-8 symbol names =3D =46rom the JVMTI callbacks symbol names are reported in modified UTF-8 stri= ngs.=20 We need to check whether Oprofile and additional tools do support this, and= =20 if not where and how a translation needs to be done. =3D> binutils and ELF also supports UTF-8 strings. Oprofile should do this corre= ctly. (needs testing) =3D Combining ELFs =3D What happens if more than one profiling session is carried out on the same = VM process?=20 Options: Unified ELF file or separate file. Pro Unified: =2D Efficient =2D Single place for ELFs =2D maybe better for analysis to have a file with all generated code includ= ed Con Unified: =2D Collision when PIDs wrap around? =2D Collision if address space reused =2D If the produced ELF file does not belong to one unique profiling sessio= n,=20 what is the lifetime of this file? =3D> Multiple profiling sessions on the same process should be rather seldom, so= this=20 is an optimization for a seldom situation which may cause much trouble for = the=20 usual situation. A separate ELF file will be generated for each profiling session. =3D Directory permissions =3D It cannot be expected that the JVM runs under root privileges. The JIT-dump= is=20 written within the JVM process context. The directory /var/lib/oprofile=20 is not world writable. =3D> /var/lib/oprofile/jitdump needs to have the correct permissions. =3D Recompiled method bytecode =3D The JVM may decide to recompile a Java method to do further optimizations. = This=20 may lead to the reuse of a previously used address range. There is currentl= y no=20 concept for address range reuse in the the Oprofile architecture. Possible resolutions: =2D Improve Oprofile architecture =2D JVM should not reuse memory (talk with the JVM/JIT people) =2D Mark methods which have overlapped address range =3D> Currently the JVM (IBM J9 JIT) is guaranteed not to reuse addresses space. =3D Library interface or sockets or file interface =3D The library libopagent must be provided for 31/32 and 64 bit on 64 bit plat= forms=20 (multilib). Providing multiple libraries is different among distributions, = so it=20 is up to the distributors packager to properly provide both library version= s. Idea: =2D Drop the library approach and use a (unix domain) socket based interfac= e instead=20 the C call interface. Performance problem, overhead? =2D Drop the library approach and specify the file format of the JIT-Dump f= iles=20 and provide header files and let the JVM agents write directly. =3D> Stay with library. Sockets mean much overhead during sampling. The file for= mat of=20 the JIT-dump file will be less stable now. New aspects can be brought in wi= th the=20 library in a compatible manner, e.g. with different library versions which = resolves=20 the dynamic linker for us. =3D Source Line numbers =3D It would be nice to have source line numbers and filenames for the Java cod= e,=20 like the debug information that is usable for C binaries. =3D> That is not feasible yet, because the JVM/JIT does not provide this informa= tion. Maybe we have this on other JITs? References =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D [OPI] Oprofile Internals Manual [OPU] Oprofile User Manual [JY] Design proposal of Jason Yeh posted on Sourceforge on 19th May 2006. [JVMTI] Specification of the Java Virtual Machine Tool Interface, see:=20 http://java.sun.com/j2se/1.5.0/docs/guide/jvmti/jvmti.html [JL0502] Comment on the initial design proposal in 2005 by John Levon: http://osdir.com/ml/oprofile/2005-02/msg00016.html [JL0505] =E2=80=9CJava patch status=E2=80=9D summary mail by John Levon 2005-05-04: http://marc.info/?l=3Doprofile-list&m=3D111523287121847&w=3D2 [MM0505] =E2=80=9CJava patch status=E2=80=9D follow-up Mail by Massimiliano Mantione= about Mono integration and=20 code ownership of the agent implementations 2005-05-05: http://marc.info/?l=3Doprofile-list&m=3D111527574606700&w=3D2 =2D-=20 Jens Wilke=20 Linux on System z - Application Development Tools=20 phone +49-(0)7031-16-3936 - tl *120-3936 - email jen...@de...=20 IBM Germany Lab, Schoenaicher Str. 220, 71032 Boeblingen=20 |
From: Jens W. <jen...@de...> - 2007-05-11 17:48:20
|
This patch adds the program opjit2elf which converts a JIT-Dump file to an ELF file. Makefile.am | 1 configure.in | 1 opjit2elf/Makefile.am | 5 opjit2elf/opjit2elf.c | 610 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 617 insertions(+) --- oprofile-0.9.2/Makefile.am 2005-03-26 02:10:24.000000000 +0100 +++ oprofile-jitadaptions/Makefile.am 2007-05-10 12:43:10.000000000 +0200 @@ -12,6 +12,7 @@ SUBDIRS = \ utils \ libregex \ libpp \ + opjit2elf \ pp \ events \ doc \ --- oprofile-0.9.2/configure.in 2006-09-15 20:29:40.000000000 +0200 +++ oprofile-jitadaptions/configure.in 2007-05-11 16:19:44.000000000 +0200 @@ -235,6 +235,7 @@ AC_OUTPUT(Makefile \ doc/opimport.1 \ doc/srcdoc/Doxyfile \ libpp/Makefile \ + opjit2elf/Makefile \ pp/Makefile \ gui/Makefile \ gui/ui/Makefile \ --- oprofile-0.9.2/opjit2elf/Makefile.am 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/opjit2elf/Makefile.am 2007-05-10 12:42:51.000000000 +0200 @@ -0,0 +1,5 @@ +bin_PROGRAMS = opjit2elf + +LIBS=@BFD_LIBS@ + +opjit2elf_SOURCES = opjit2elf.c --- oprofile-0.9.2/opjit2elf/opjit2elf.c 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/opjit2elf/opjit2elf.c 2007-05-11 16:12:22.000000000 +0200 @@ -0,0 +1,610 @@ +/** + * @file opjit2elf.c + * Convert a jit dump file to an ELF file for a given vma region + * + * @remark Copyright 2007 OProfile authors + * @remark Read the file COPYING + * + * @author Jens Wilke + * + * Copyright IBM Corporation 2007 + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <bfd.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +/* Structure that contains all information + * for one function entry in the jit dump file. + * the jit dump file gets mmapped and code and + * symbol_name point directly into the file */ +struct jitentry { + struct jitentry * next; + long long code_addr; + void * code; + int code_size; + char * symbol_name; +}; + +/* list head */ +static struct jitentry *jitentry_list = NULL; + +/* jit dump header information */ +static int version; +static PTR dump_bfd_target; +static int dump_bfd_arch; +static int dump_bfd_mach; +static bfd *cur_bfd; + +/* count of jitentries in the list */ +static int entry_count = 0; +/* array pointing to all jit entries, sorted by symbol names */ +static struct jitentry ** entries_symbols_ascending; +/* array pointing to all jit entries sorted by address */ +static struct jitentry ** entries_address_ascending; + +/* debug flag, print some information */ +static int debug; + +/* + * Error handling: + * In case of an unexpected problem we just call abort() and exit immediatelly. + * We are called for a single jitdump file by the opcontrol shell script. We don't + * need to do error recovery or error propagation + */ +void abort() { + exit(1); +} + +void abort_malloc() { + perror("malloc"); + abort(); +} + +void bfd_abort(char *s) { + bfd_perror(s); + abort(); +} + +/* + ********************************************************* parse JIT-Dump file + */ + +/* parse size in uint32 from jit dump and move ptr */ +int read_size(PTR *ptr, PTR end) { + uint32_t * p0 = *ptr; + uint32_t * pw = *ptr; + pw++; + if ((PTR)pw > end) { + fprintf(stderr, "Format error: end reached while reading uint32_t\n"); + abort(); + } + *ptr = pw; + return * p0; +} + +/* parse an address from jit dump and move ptr */ +long long read_ptr(PTR * ptr, PTR end) { + uint64_t * p0 = *ptr; + uint64_t * pw =*ptr; + pw++; + if ((PTR) pw > end) { + fprintf(stderr, "Format error: end reached while reading uint64_t\n"); + abort(); + } + *ptr = pw; + return *p0; +} + +/* read a character from jit dump and move ptr */ +char read_char(PTR *ptr, PTR end) { + char * p0 = *ptr; + char * pw = *ptr; + pw++; + if ((PTR) pw > end) { + fprintf(stderr, "Format error: end reached while reading char\n"); + abort(); + } + *ptr = pw; + return *p0; +} + +/* read a line from jit dump and move ptr */ +char * read_line(PTR *ptr, PTR end) { + char * pw = *ptr; + char * p0 = *ptr; + char * res; + int size; + + while (*pw != '\n') { + if ((PTR) pw > end) { + fprintf(stderr, "Format error: end reached while reading line\n"); + abort(); + } + pw ++; + } + size = pw-p0; + res = malloc(size+1); + if (res == 0) { + abort_malloc(); + } + memcpy(res, p0, size); + res[size] = 0; + *ptr = pw+1; + if (debug) { + fprintf(stderr, "header_readline: %s\n", res); + } + return res; +} + +/* parse a type 0 record and add the entry to the jitentry list */ +void parse_record0(PTR ptr, int size) { + struct jitentry * entry; + PTR end = ptr + size; + + entry = malloc(sizeof(struct jitentry)); + if (entry == 0) { + abort_malloc(); + } + // init struct + entry->next = NULL; + entry->symbol_name = ptr; + ptr += strnlen(ptr,size)+1; + entry->code_addr = read_ptr(&ptr, end); + entry->code_size = read_size(&ptr, end); + entry->code = ptr; + // build list + entry->next = jitentry_list; + jitentry_list = entry; + if (debug) { + fprintf(stderr,"record0: name=%s, code_addr=%llx, code_size=%i\n", + entry->symbol_name, entry->code_addr, entry->code_size); + } + if (((char *)ptr + entry->code_size) != end) { + fprintf(stderr,"length mismatch"); + abort(); + } +} + +/* parse all entries in the jit dump file and build jitentry_list. + * the code needs to check always whether there is enough + * to read remaining. this is because the file may be written to + * concurrently. */ +void parse_entries(PTR * ptr, PTR end) { + char type; + int size; + PTR pw = *ptr; + + while ((pw+1+sizeof(uint32_t))<end) { + type = read_char(&pw, end); + size =read_size(&pw, end); + if (debug) { + fprintf(stdout,"jitentry: %x: type=%i, size=%i \n", pw, type, size); + } + if (size<0) { + fprintf(stderr, "wrong size\n"); + abort(); + } + if (pw+size<=end) { + if (type==0) { + parse_record0(pw, size); + } else { + fprintf(stderr, "unknown record type\n"); + abort(); + } + } + pw += size; + } + *ptr = pw; +} + +/* retrun value part of a line like "Param: <value>" */ +char* value(char *s) { + while (*s != ':' && *s) { + s++; + } + if (*s) { + return s+1; + } + fprintf(stderr, "Value expected in line: %s\n", s); + abort(); + return 0; +} + +/* check whether the parameter matches p */ +int parm(char *p, char *l) { + int size = strlen(p); + return strncmp(p, l, size)==0; +} + +/* parse the jit dump header information */ +void parse_header(PTR * ptr, PTR end) { + char * magic; + char * line; + char *cmp; + PTR pw = *ptr; + int rc; + + magic = read_line(&pw, end); + if (strcmp(magic, "Oprofile Agent JIT-Dump")) { + fprintf(stderr, "Wrong file magic\n"); + abort(); + } + line = read_line(&pw, end); + dump_bfd_mach, dump_bfd_arch = -1; + while (*line != 0) { + if (parm("Version", line)) { + rc = sscanf(value(line), "%i", &version); + if (rc<1) { + fprintf(stderr, "header error: can't scan version integer\n"); + abort(); + } + } else if (parm("BFD-Target", line)) { + dump_bfd_target = value(line); + } else if (parm("BFD-Mach", line)) { + rc = sscanf(value(line), "%i", &dump_bfd_mach); + if (rc<1) { + fprintf(stderr, "header error: can't scan bfd-mach integer\n"); + abort(); + } + } else if (parm("BFD-Arch", line)) { + rc = sscanf(value(line), "%i", &dump_bfd_arch); + if (rc<1) { + fprintf(stderr, "header error: can't scan bfd-arch integer\n"); + abort(); + } + } + line = read_line(&pw, end); + } + if (version != 1) { + fprintf(stderr, "Wrong version"); + abort(); + } + if (dump_bfd_arch < 0 || dump_bfd_mach < 0) { + fprintf(stderr, "BFD-Arch / BFD-Mach value missing in JIT-Dump"); + abort(); + } + if (debug) { + fprintf(stdout, "header: bfd-target=%s, bfd-mach=%i, bfd-arch=%i\n", + dump_bfd_target, dump_bfd_mach, dump_bfd_arch); + } + *ptr = pw; +} + +/* parse the complete file and set global vars */ +void parse_all(PTR start, PTR end) { + PTR ptr = start; + parse_header(&ptr, end); + parse_entries(&ptr, end); +} + +/* + ****************************************** sort symbols and disambiguate names + */ + +/* count the entries in the jitentry_list */ +int count_entries() { + int cnt = 0; + struct jitentry *entry; + entry = jitentry_list; + while (entry!=NULL) { + entry = entry->next; + cnt ++; + } + return cnt; +} + +void fill_entry_array(struct jitentry *entries[]) { + int cnt = 0; + struct jitentry *entry; + entry = jitentry_list; + + while (entry!=NULL) { + entries[cnt++] = entry; + entry = entry->next; + } +} + +/* create an array pointin to the jitentry structures which is sorted according + * to the comparator rule given by parameter compar */ +struct jitentry ** create_sorted_array(int(*compar)(const void *, const void *)) { + struct jitentry ** array = malloc(sizeof(struct jitentry *)*entry_count); + if (array == NULL) { + abort_malloc(); + } + fill_entry_array(array); + qsort(array, entry_count, sizeof(struct jitentry *), compar); + return array; + } + +/* comparator method for qsort which sorts jitentries by symbol_name */ +int cmp_symbolname(const void *a, const void *b) { + struct jitentry *a0 = *(struct jitentry **) a; + struct jitentry *b0 = *(struct jitentry **) b; + return strcmp(a0->symbol_name, b0->symbol_name); +} + +/* comparator method for qsort which sorts jitentries by address */ +int cmp_address(const void *a, const void *b) { + struct jitentry *a0 = *(struct jitentry **) a; + struct jitentry *b0 = *(struct jitentry **) b; + if (a0->code_addr < b0->code_addr) { + return -1; + } + if (a0->code_addr == b0->code_addr) { + return 0; + } + return 1; +} + +/* allocate, populate and sort the jitentry arrays */ +void create_arrays(void) { + if (debug) { + fprintf(stderr,"creating arrays...\n"); + } + entry_count = count_entries(); + entries_symbols_ascending = create_sorted_array(cmp_symbolname); + entries_address_ascending = create_sorted_array(cmp_address); +} + +/* add a suffix to the name to differenciate it */ +char * replacement_name(char * s, int i) { + int cnt = 1; + int j = i; + char * res; + + while (j=j/10) { + cnt++; + } + cnt += 2+strlen(s); + res = malloc(cnt); + if (res == NULL) { + abort_malloc(); + } + snprintf(res, cnt, "%s.%i", s, i); + return res; +} + +/* + * scan through the sorted array and replace identical symbol names by unique ones + * by adding a counter value. + * FIXME: not 100% save in the case that the symbol with added suffix is + * also a valid symbol name in the data set + */ +void disambiguate_symbol_names(void) { + int i, j, cnt; + struct jitentry* a; + struct jitentry* b; + + if (debug) { + fprintf(stderr,"disambiguate...\n"); + } + for (j=1; j<entry_count; j++) { + i = j-1; + a =entries_symbols_ascending[i]; + cnt = 1; + do { + b =entries_symbols_ascending[j]; + if (strcmp(a->symbol_name, b->symbol_name)==0) { + b->symbol_name = replacement_name(b->symbol_name, cnt); + j++; + cnt++; + } else { + break; + } + } while (j<entry_count); + } +} + +/* + ******************************************************** create&write ELF file + */ + +/* + * Copy all functions code that are within vma_start and size to the section. + * start_idx and end_idx will be set to indices into entries_address_ascending array. + * start_idx is the index of the first copyied function and end_idx is the index of the + * last copied function + */ +void fill_section( + long long vma_start, + PTR section, + int size, + int *start_idx, + int *end_idx) { + + int i; + struct jitentry* e; + long long vma_end = vma_start + size; + *start_idx = -1; + *end_idx = -1; + + for (i=0; i<entry_count; i++) { + e = entries_address_ascending[i]; + if (e->code_addr >= vma_end) { + break; + } + if (e->code_addr >= vma_start) { + if (*start_idx == -1) { + *start_idx = i; + } + if (e->code_addr + e->code_size > vma_end) { + // should never happen + fprintf(stderr, "Error: sym %s end after vma_end", e->symbol_name); + } else { + *end_idx = i; + memcpy(section + (e->code_addr - vma_start), e->code, e->code_size); + } + } + } + if (*end_idx == -1) { + fprintf(stderr,"no symbols in requested VMA region found\n"); + abort(); + } +} + +/* Create the symbols and fill the syms array for all functions + * from start_idx to end_idx pointing into entries_address_ascending array */ +void fill_symtab( + asymbol **syms, + asection *section, + long long vma_start, + int start_idx, + int end_idx) { + int i,j; + struct jitentry* e; + asymbol *s; + + for (j=start_idx, i=0;j<=end_idx; i++,j++) { + e = entries_address_ascending[j]; + s = bfd_make_empty_symbol(cur_bfd); + if (s==NULL) { + bfd_abort("bfd_make_empty_symbol"); + } + s->name = e->symbol_name; + s->section = section; + s->flags = BSF_GLOBAL | BSF_FUNCTION; + s->value = e->code_addr - vma_start; + if (debug) { + fprintf(stderr,"add sym: name=%s, value=%llx\n", e->symbol_name,s->value); + } + syms[i] = s; + } +} + +/* create the .text section */ +void create_section( + long long vma_start, + long long vma_end) { + asection *section; + asymbol **syms; + int start_idx, end_idx; + PTR code; + int size = vma_end - vma_start; + bfd_boolean r; + int sym_count; + + if (debug) { + fprintf(stderr,"create section...\n"); + } + section = bfd_make_section(cur_bfd, ".text"); + if (section == NULL) { + bfd_abort("bfd_make_section"); + } + code = malloc(size); + if (code == NULL) { + abort_malloc(); + } + /* we may not fill up the complete, + * make sure we store not random content (for compress) */ + bzero(code, size); + fill_section(vma_start, code, size, &start_idx, &end_idx); + sym_count = end_idx-start_idx+1; + fprintf(stdout, "Found %i symbols\n", sym_count); + syms = malloc(sizeof(asymbol *)*(sym_count+1)); + if (syms == NULL) { + abort_malloc(); + } + syms[sym_count] = NULL; + fill_symtab(syms, section, vma_start, start_idx, end_idx); + r = bfd_set_symtab(cur_bfd, syms, sym_count); + if (!r) { + bfd_abort("bfd_set_symtab"); + } + r = bfd_set_section_size(cur_bfd, section, size); + if (!r) { + bfd_abort("bfd_set_section_size"); + } + r = bfd_set_section_vma(cur_bfd, section, 0); + if (!r) { + bfd_abort("bfd_set_section_vma"); + } + r = bfd_set_section_flags(cur_bfd, section, + SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_HAS_CONTENTS); + if (!r) { + bfd_abort("bfd_set_section_flags"); + } + r = bfd_set_section_contents(cur_bfd, section, + code, (file_ptr)0, (bfd_size_type)size); + if (!r) { + bfd_abort("bfd_set_section_contents"); + } +} + +/* create the elf file */ +void open_elf(char * filename) { + bfd_boolean r; + + cur_bfd = bfd_openw(filename ,NULL); + if (cur_bfd==NULL) { + bfd_abort("bfd_openw"); + } + r = bfd_set_format(cur_bfd, bfd_object); + if (!r) { + bfd_abort("bfd_set_format"); + } + r = bfd_set_arch_mach(cur_bfd, dump_bfd_arch, dump_bfd_mach); +} + +/* + ************************************************************************ main + */ + +int main(int argc, char** argv) { + long long vma_start; + long long vma_end; + char * dumpfile; + char * elffile; + int dumpfd; + PTR jitdump; + struct stat stat; + int rc; + + debug = 0; + if (argc>0 && strcmp(argv[1],"-d")==0) { + debug = 1; + argc--; + argv++; + } + + if (argc!=5) { + fprintf(stderr, "Usage: jit2elf [-d] <dumpfile> <elffile> <vma_start> <vma_end>\n"); + return 1; + } + dumpfile = argv[1]; + elffile = argv[2]; + sscanf(argv[3], "%llx", &vma_start); + sscanf(argv[4], "%llx", &vma_end); + + dumpfd = open(dumpfile, O_RDONLY); + if (dumpfd<0) { + perror("open JIT-Dump"); + abort(); + } + rc = fstat(dumpfd, &stat); + if (rc<0) { + perror("fstat"); + abort(); + } + jitdump = mmap(0, stat.st_size, PROT_READ, MAP_PRIVATE, dumpfd, 0); + if (!jitdump) { + perror("mmap"); + abort(); + } + + parse_all(jitdump, jitdump + stat.st_size); + create_arrays(); + disambiguate_symbol_names(); + open_elf(elffile); + create_section(vma_start, vma_end); + bfd_close(cur_bfd); + return 0; +} |
From: Jens W. <jen...@de...> - 2007-05-11 17:48:25
|
This patch adds the "glue" code to opcontrol that generates the ELF files and cleans up the jitdump directory. utils/opcontrol | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) --- oprofile-0.9.2/utils/opcontrol 2006-03-29 23:54:07.000000000 +0200 +++ oprofile-jitadaptions/utils/opcontrol 2007-05-11 16:02:41.000000000 +0200 @@ -6,7 +6,9 @@ # Copyright 2002 # Read the file COPYING # -# Authors: John Levon, Philippe Elie, Will Cohen +# Authors: John Levon, Philippe Elie, Will Cohen, Jens Wilke +# +# Copyright IBM Corporation 2007 # # NOTE: This script should be as shell independent as possible @@ -1216,9 +1218,31 @@ do_start_daemon() echo "Daemon started." } +# create jitdump directory and remove any old files from +# a previous run +prep_jitdump() { + local dumpdir=$DIR/jitdump + test -d $dumpdir || { + mkdir -p $dumpdir; + chmod 777 $dumpdir; + return; + } + # VMs may already be running when profiling is started, so + # remove only dump files that are not in use + for I in $dumpdir/*; do + test -f $I || continue; + local pid=`basename $I .dump`; + if test -d /proc/$pid; then + local files=`find /proc/$pid/fd -lname $I`; + test -n "$files" && continue; + fi + rm -f $I; + done +} do_start() { + prep_jitdump; if test "$KERNEL_SUPPORT" = "yes"; then echo 1 >$MOUNT/enable fi @@ -1339,12 +1363,36 @@ do_dump_data() return 0; } +do_jit2elf() { + OPJIT2ELF="$OPDIR/opjit2elf" + DEST=$SAMPLES_DIR/current/{anon} + JITDIR=$DIR/jitdump; + create_dir $DEST; + + # this gives us a list of the regions with anon samples, e.g.: + # 7189.0xa4741000.0xa47c1000 7189.0xa53f2000.0xa5472000 11835.0x96d74000.0x96df4000 + local anonlist="`find $SAMPLES_DIR/current -path \*{anon}/\* -type d -printf '%f '`" + test -n "$anonlist" && for I in $anonlist; do + local pid=`expr $I : '\(.*\)\..*\.`; + local start=`expr $I : '.*\.0x\(.*\)\.'`; + local end=`expr $I : '.*\..*\.0x\(.*\)'`; + local jitdump=$JITDIR/$pid.dump + if test -f $jitdump; then + echo found jitdump $jitdump, extracting region $start-$end + $OPJIT2ELF $jitdump $DEST/$I $start $end + fi + done + # FIXME: we may run prep_jitdump here to clean the directory but let's keep them + # at the moment (for debugging...) +} + # do_dump # returns 0 if successful # exits if unsuccessful do_dump() { do_dump_data + do_jit2elf if test $? -ne 0 -a "$ONLY_DUMP" = "yes"; then echo "Unable to complete dump of oprofile data: is the oprofile daemon running?" >& 2 exit 1; @@ -1402,6 +1450,11 @@ do_reset() move_and_remove $SAMPLES_DIR/current/{kern} move_and_remove $SAMPLES_DIR/current/{root} + # clear temp directory for jitted code + move_and_remove $DIR/jitdump + move_and_remove $SAMPLES_DIR/current/{anon} + prep_jitdump; + hup_daemon } |
From: Maynard J. <may...@us...> - 2007-05-15 20:25:33
|
Jens Wilke wrote: >This patch adds the "glue" code to opcontrol that generates the ELF files >and cleans up the jitdump directory. > > utils/opcontrol | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 54 insertions(+), 1 deletion(-) > >--- oprofile-0.9.2/utils/opcontrol 2006-03-29 23:54:07.000000000 +0200 >+++ oprofile-jitadaptions/utils/opcontrol 2007-05-11 16:02:41.000000000 +0200 >@@ -6,7 +6,9 @@ > # Copyright 2002 > > [snip] >+ # this gives us a list of the regions with anon samples, e.g.: >+ # 7189.0xa4741000.0xa47c1000 7189.0xa53f2000.0xa5472000 11835.0x96d74000.0x96df4000 >+ local anonlist="`find $SAMPLES_DIR/current -path \*{anon}/\* -type d -printf '%f '`" >+ test -n "$anonlist" && for I in $anonlist; do > > bash complained about this next line missing a " ' ". It should be: local pid=`expr $I : '\(.*\)\..*\.'`; >+ local pid=`expr $I : '\(.*\)\..*\.`; >+ local start=`expr $I : '.*\.0x\(.*\)\.'`; >+ local end=`expr $I : '.*\..*\.0x\(.*\)'`; >+ local jitdump=$JITDIR/$pid.dump >+ if test -f $jitdump; then >+ echo found jitdump $jitdump, extracting region $start-$end >+ $OPJIT2ELF $jitdump $DEST/$I $start $end >+ fi >+ done > > [snip] |
From: Jens W. <jen...@de...> - 2007-05-11 17:48:27
|
This patch changes the parsing of the directories following the {dep} so that we point now to the correct ELF image for the anonymous regions. libpp/parse_filename.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) --- oprofile-0.9.2/libpp/parse_filename.cpp 2005-05-02 17:07:02.000000000 +0200 +++ oprofile-jitadaptions/libpp/parse_filename.cpp 2007-05-10 14:36:26.000000000 +0200 @@ -57,15 +57,18 @@ parsed_filename parse_event_spec(string /** * @param component path component + * @param basse_dir is filled with the base directory with a trailing and leading / * * remove from path_component all directory left to {root}, {kern} or {anon} */ -void remove_base_dir(vector<string> & path) +void remove_base_dir(vector<string> & path, string & base_dir) { vector<string>::iterator it; + base_dir = "/"; for (it = path.begin(); it != path.end(); ++it) { if (*it == "{root}" || *it == "{kern}" || *it == "{anon}") break; + base_dir += *it+"/"; } path.erase(path.begin(), it); @@ -109,6 +112,7 @@ parsed_filename parse_filename(string co } string event_spec = filename.substr(pos + 1); string filename_spec = filename.substr(0, pos); + string base_dir = ""; parsed_filename result = parse_event_spec(event_spec); @@ -116,7 +120,7 @@ parsed_filename parse_filename(string co vector<string> path = separate_token(filename_spec, '/'); - remove_base_dir(path); + remove_base_dir(path, base_dir); // pp_interface PP:3.19 to PP:3.23 path must start either with {root} // or {kern} and we must found at least 2 component, remove_base_dir() @@ -158,7 +162,9 @@ parsed_filename parse_filename(string co break; if (anon) { - result.lib_image = parse_anon(path[i++]); + // result.lib_image = parse_anon(path[i++]); + // point to a (potentially) generated elf file from opjit2elf + result.lib_image = base_dir + "{anon}/"+path[i++]; break; } result.lib_image += "/" + path[i]; |
From: Jens W. <jen...@de...> - 2007-05-11 17:48:28
|
It may happen that the process is already terminated when we process the samples in the daemon. This patch just adds an error message, so we see the cause in the Oprofile log file. daemon/opd_anon.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) --- oprofile-0.9.2/daemon/opd_anon.c 2006-03-19 20:17:01.000000000 +0100 +++ oprofile-jitadaptions/daemon/opd_anon.c 2007-05-10 14:39:42.000000000 +0200 @@ -135,8 +135,10 @@ static void get_anon_maps(struct transie snprintf(buf, PATH_MAX, "/proc/%d/maps", trans->tgid); fp = fopen(buf, "r"); - if (!fp) + if (!fp) { + perror(buf); return; + } while (fgets(buf, PATH_MAX, fp) != NULL) { char tmp[20+1]; |
From: John L. <le...@mo...> - 2007-05-20 18:04:26
|
On Fri, May 11, 2007 at 07:44:06PM +0200, Jens Wilke wrote: > It may happen that the process is already terminated when we process > the samples in the daemon. This patch just adds an error message, so > we see the cause in the Oprofile log file. If it's normal behaviour we should only have an error message when we are in verbose mode. I'll take a patch to do that. regards john |
From: Jens W. <jen...@de...> - 2007-05-11 17:48:32
|
This patch contains the example JVMTI implementation. Don't know whether the destination exta/jvmti is a good idea and whether we need to add this on "make install" also. extra/jvmti/Makefile | 18 +++++ extra/jvmti/README | 30 +++++++++ extra/jvmti/jvmti_oprofile.c | 140 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) --- oprofile-0.9.2/extra/jvmti/jvmti_oprofile.c 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/extra/jvmti/jvmti_oprofile.c 2007-05-11 11:01:06.000000000 +0200 @@ -0,0 +1,140 @@ +/** + * @file jvmti_oprofile.c + * JVMTI agent implementation to report jitted JVM code to Oprofile + * + * @remark Copyright 2007 OProfile authors + * @remark Read the file COPYING + * + * @author Jens Wilke + * + * Copyright IBM Corporation 2007 + * + */ + +#include <stdio.h> +#include <jvmti.h> +#include <string.h> +#include <libopagent.h> + +void JNICALL +cbCompiledMethodLoad(jvmtiEnv *jvmti, + jmethodID method, + jint code_size, + const void* code_addr, + jint map_length, + const jvmtiAddrLocationMap* map, + const void* compile_info) +{ + /* Get the declaring class of the method */ + jclass declaringClass; + char * classSig = NULL; + char * methodName = NULL; + char * methodSig = NULL; + jvmtiError err; + + err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &declaringClass); + if (err != JVMTI_ERROR_NONE) { + fprintf(stderr, "Error, GetMethodDeclaringClass, rc=%i\n", err); + goto cleanup; + } + err = (*jvmti)->GetClassSignature(jvmti, declaringClass, &classSig, NULL); + if (err != JVMTI_ERROR_NONE) { + fprintf(stderr, "Error, GetClassSignature, rc=%i\n", err); + goto cleanup; + } + err = (*jvmti)->GetMethodName(jvmti, method, &methodName, &methodSig, NULL); + if (err != JVMTI_ERROR_NONE) { + fprintf(stderr, "Error, GeMethodName, rc=%i\n", err); + goto cleanup; + } + + /* DEBUG + fprintf(stderr, "class=%s, methodName=%s, methodSig=%s, addr=%x, size=%i \n", classSig, methodName, methodSig, code_addr, code_size); + */ + { + int cnt = strlen(methodName)+strlen(classSig)+2; + char buf[cnt]; + + /* cut away the ; */ + classSig[strlen(classSig)-1] = 0; + sprintf(buf, "%s/%s", classSig+1, methodName); + op_write_native_code(buf, code_addr, code_size); + } + + + cleanup: + if (classSig!=NULL) { (*jvmti)->Deallocate (jvmti, classSig); } + if (methodName!=NULL) { (*jvmti)->Deallocate (jvmti, methodName); } + if (methodSig!=NULL) { (*jvmti)->Deallocate (jvmti, methodSig); } +} + +void JNICALL cbCompiledMethodUnload (jvmtiEnv *jvmti_env, jmethodID method, + const void* code_addr) +{ +} + +void JNICALL cbDynamicCodeGenerated (jvmtiEnv *jvmti_env, const char* name, + const void* code_addr, jint code_size) +{ +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) +{ + jint rc; + jvmtiEnv *jvmti = NULL; + jvmtiEventCallbacks callbacks; + jvmtiCapabilities caps; + jvmtiError error; + int err; + + fprintf(stderr, "jvmti_oprofile: agent loading\n"); + err = op_open_agent(); + if (err) { + fprintf(stderr, "ERROR: op_open_agent, rc=%i\n", err); + return -1; + } + + /* Get the jvmti environment */ + rc = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1); + if (rc != JNI_OK) { + fprintf(stderr, "ERROR: Unable to access JVMTI Version 1, rc=%i\n", rc); + return -1; + } + rc = (*jvmti)->GetCapabilities(jvmti, &caps); + if (rc != JNI_OK) { + fprintf(stderr, "ERROR: GetCapabilities, rc=%i\n", rc); + return -1; + } + caps.can_generate_compiled_method_load_events = 1; + rc = (*jvmti)->AddCapabilities(jvmti, &caps); + if (rc != JNI_OK) { + fprintf(stderr, "ERROR: AddCapabilities, rc=%i\n", rc); + return -1; + } + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.CompiledMethodLoad = cbCompiledMethodLoad; + callbacks.CompiledMethodUnload = cbCompiledMethodUnload; + callbacks.DynamicCodeGenerated = cbDynamicCodeGenerated; + rc = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks)); + if (rc != JNI_OK) { + fprintf(stderr, "ERROR: SetEventCallbacks, rc=%i\n", rc); + return -1; + } + + rc = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, + JVMTI_EVENT_COMPILED_METHOD_LOAD, (jthread)NULL); + rc = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, + JVMTI_EVENT_COMPILED_METHOD_UNLOAD, (jthread)NULL); + rc = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, + JVMTI_EVENT_DYNAMIC_CODE_GENERATED, (jthread)NULL); + fprintf(stderr, "jvmti_oprofile: initialized, producing output of jitted methods\n"); + + return 0; +} + +JNIEXPORT void JNICALL +Agent_OnUnload(JavaVM *vm) +{ + op_close_agent(); +} --- oprofile-0.9.2/extra/jvmti/README 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/extra/jvmti/README 2007-05-11 10:20:19.000000000 +0200 @@ -0,0 +1,30 @@ +What is this? +------------- + +This is an JVMTI agent implementation that works with JDKs 1.5 or +higher. It retrieves names and assembly code of jitted methods +of the JVM, for later display in an Oprofile report. + +Installation +------------ + +Edit the directory of your JDK and Oprofile installation in +the Makefile and run make. + +There is no make install target, however, it makes sense to +keep the created lib libjvmti_oprofile.so together with +the JDK that it was compiled for. + +Usage +----- + +Start the JDK with the extra parameter: + +-agentpath:<installdir>/libjvmti_oprofile.so + +The modified Oprofile can be used as before. For short running +java workloads the buffer watershed must be reduced, because +the Oprofile daemon retrieves additional information from the process +when the buffer is processed. E.g. start the sampling with the parameters: + +opcontrol --start --buffer-size=131072 --buffer-watershed=131000 --- oprofile-0.9.2/extra/jvmti/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/extra/jvmti/Makefile 2007-05-11 10:03:14.000000000 +0200 @@ -0,0 +1,18 @@ +# replace this to fit your installation +JDK=${JAVA_HOME} +OPROFILE=/opt/oprofile-exp + +OPAGENTLIB=$(OPROFILE)/lib +OPLIBFLAGS=-lopagent -L$(OPAGENTLIB) + +# for the sun jdk we need the addition os include -I$(JDK)/include/linux +# see http://java.sun.com/products/jdk/faq/jni-j2sdk-faq.html#jni_md +INCLUDES=-I$(OPROFILE)/include -I$(JDK)/include -I$(JDK)/include/linux + +all: libjvmti_oprofile.so + +clean: + rm libjvmti_oprofile.so + +libjvmti_oprofile.so: jvmti_oprofile.c Makefile + gcc -shared -o $@ -Wl,-soname=$@ -static-libgcc -lc $(INCLUDES) $(OPLIBFLAGS) $< |
From: Jens W. <jen...@de...> - 2007-05-11 17:48:35
|
This patch contains the libopagent implementation. This needs work, I think we should have a seperate configure script in this directory to better support multilib builds. Makefile.am | 1 configure.in | 1 libopagent/Makefile.in | 52 +++++++++++++++++++++++++++++ libopagent/bfddefines.c | 40 ++++++++++++++++++++++ libopagent/libopagent.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ libopagent/libopagent.h | 19 ++++++++++ 6 files changed, 198 insertions(+) --- oprofile-0.9.2/Makefile.am 2005-03-26 02:10:24.000000000 +0100 +++ oprofile-jitadaptions/Makefile.am 2007-05-10 12:43:10.000000000 +0200 @@ -3,6 +3,7 @@ SUBDIRS = \ m4 \ libutil \ libop \ + libopagent \ libdb \ libutil++ \ libopt++ \ --- oprofile-0.9.2/configure.in 2006-09-15 20:29:40.000000000 +0200 +++ oprofile-jitadaptions/configure.in 2007-05-11 16:19:44.000000000 +0200 @@ -209,6 +209,7 @@ AC_OUTPUT(Makefile \ libutil++/tests/Makefile \ libop/Makefile \ libop/tests/Makefile \ + libopagent/Makefile \ libopt++/Makefile \ libdb/Makefile \ libdb/tests/Makefile \ --- oprofile-0.9.2/libopagent/bfddefines.c 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/libopagent/bfddefines.c 2007-05-11 15:55:47.000000000 +0200 @@ -0,0 +1,40 @@ +/** + * @file bfddefines.c + * Write out defines for BFD arch and mach of the given binary + * + * @remark Copyright 2007 OProfile authors + * @remark Read the file COPYING + * + * @author Jens Wilke + * + * Copyright IBM Corporation 2007 + * + */ + +#include <stdio.h> +#include <bfd.h> + +int main(int argc, char** args) { + bfd *bfd; + int t; + bfd_boolean r; + + bfd_init(); + bfd = bfd_openr(args[1], NULL); + if (bfd==NULL) { + bfd_perror("bfd_open"); + return 1; + } + r = bfd_check_format(bfd, bfd_object); + if (!r) { + bfd_perror("bfd_get_arch"); + return 1; + } + printf("/* automatically generated by bfddefines, do not edit*/\n"); + printf("#define BFD_TARGET_NAME \"%s\"\n", bfd->xvec->name); + printf("#define BFD_ARCH \"%i\"\n", bfd_get_arch(bfd)); + printf("#define BFD_MACH \"%i\"\n", bfd_get_mach(bfd)); + printf("#define BFD_PRINTABLE_NAME \"%s\"\n", bfd_printable_name(bfd)); + + return 0; +} --- oprofile-0.9.2/libopagent/libopagent.c 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/libopagent/libopagent.c 2007-05-11 11:02:51.000000000 +0200 @@ -0,0 +1,85 @@ +/** + * @file libopagent.c + * Interface to report symbol names and dynamically generated code to Oprofile + * + * @remark Copyright 2007 OProfile authors + * @remark Read the file COPYING + * + * @author Jens Wilke + * + * Copyright IBM Corporation 2007 + * + */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <limits.h> + +#include "libopagent.h" +#include "bfdheader.h" + +/* FIXME: default value and environment variable */ +#define AGENT_DIR "/var/lib/oprofile/jitdump" + +#define MSG_MAXLEN 20 + +static FILE * dumpfile = NULL; + +int op_open_agent() { + char tmp[PATH_MAX + 1]; + char *str; + + snprintf(tmp, PATH_MAX, "%s/%i.dump", AGENT_DIR, getpid()); + dumpfile = fopen(tmp, "w"); + if (dumpfile==NULL) { + perror(tmp); + return -1; + } + fprintf(dumpfile, "%s", "Oprofile Agent JIT-Dump\n"); + fprintf(dumpfile, "%s", "Version: 1\n"); + fprintf(dumpfile, "%s", "BFD-Target: "BFD_TARGET_NAME"\n"); + fprintf(dumpfile, "%s", "BFD-Arch: "BFD_ARCH"\n"); + fprintf(dumpfile, "%s", "BFD-Mach: "BFD_MACH"\n"); + fprintf(dumpfile, "%s", "BFD-Printable-Name: "BFD_PRINTABLE_NAME"\n"); + fputc('\n', dumpfile); + + return 0; +} + +void op_close_agent() +{ + fclose(dumpfile); +} + +int op_write_native_code( + char const * symbol_name, + const void * code, + const unsigned int size) +{ + if (dumpfile==NULL) { + fprintf(stderr, "LIBOPAGENT ERROR: op_open_agent must be called first\n"); + return -1; + } + uint32_t code_size = size; + uint64_t addr = (uint64_t) (unsigned int) code; + uint32_t total_size = sizeof(code_size) + sizeof(addr) + strlen(symbol_name)+1+size; + + /* DEBUG + fprintf(stderr, "op_write_native_code: %s, code_addr=%llx, code_size=%i\n", symbol_name, addr, size); + */ + + /* FIXME: error handling? */ + /* record id: 0 = native_code, format <total_size>, <symbol_name>, <code_size>, <code> */ + flockfile(dumpfile); + fputc_unlocked(0, dumpfile); + + fwrite_unlocked(&total_size, sizeof(total_size), 1, dumpfile); + fwrite_unlocked(symbol_name, strlen(symbol_name)+1, 1, dumpfile); + fwrite_unlocked(&addr, sizeof(addr), 1, dumpfile); + fwrite_unlocked(&code_size, sizeof(code_size), 1, dumpfile); + fwrite_unlocked(code, size, 1, dumpfile); + funlockfile(dumpfile); + return 0; +} --- oprofile-0.9.2/libopagent/libopagent.h 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/libopagent/libopagent.h 2007-05-04 15:01:34.000000000 +0200 @@ -0,0 +1,19 @@ +#ifndef _LIB_OPAGENT_H +#define _LIB_OPAGENT_H + +#include <sys/types.h> + +int op_open_agent(); + +void op_close_agent(); + +int op_write_native_code( + char const * symbol_name, + const void * code, + const unsigned int code_size); + +/* idea how to post additional info for a code address +int op_write_loader_name(const void* code_addr, char const * loader_name); +*/ + +#endif --- oprofile-0.9.2/libopagent/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/libopagent/Makefile.in 2007-05-11 15:37:01.000000000 +0200 @@ -0,0 +1,52 @@ + +# FIXME: automake? +# FIXME: versioning of library? +# FIXME: how should this look like to support multilib builds? +# maybe separate configure which also has the bfddefines magic? + +CFLAGS = @CFLAGS@ +INSTALL = @INSTALL@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +libdir = @libdir@ +includedir = @includedir@ + +SOURCES =\ + libopagent.c \ + libopagent.h + +OBJECTS = libopagent.o + +LIB = libopagent.so + +all: $(LIB) + +bfddefines: bfddefines.c + $(CC) $(CFLAGS) -lbfd $< -o $@ + +bfdheader.h: bfddefines + ./bfddefines bfddefines > $@ + +libopagent.o: bfdheader.h + +%.o: %.c + $(CC) -c -fPIC $(CFLAGS) $< -o $@ + +$(LIB): $(OBJECTS) + $(CC) -shared $(OBJECTS) -o $@ +# $(CC) -shared -Wl,-soname,$(notdir $@).$(LIB_VERSION) \ +# $(CLDFLAGS) $(OBJECTS) $(LIBS) -o $@ + +install-headers: + $(INSTALL) -D libopagent.h $(includedir)/libopagent.h + +install-exec: + $(INSTALL) -D $(LIB) $(libdir)/$(LIB) + +install: install-headers install-exec + +clean: + rm -f $(OBJECTS) $(LIB) + +distclean: clean |
From: Massimiliano M. <ma...@xi...> - 2007-05-11 19:22:31
|
To read my comments in context, I'm a Mono JIT developer... On Fri, 2007-05-11 at 19:30 +0200, Jens Wilke wrote: > [...] > - The interface of profiling agents should be generic and work > for various JVMs or JIT compilers just as Mono [JY] Yay! Thanks a lot for this :-) > The library is a generic interface for virtual machines that generate dynamic code > and to report that code and symbol information to Oprofile. > > = Interface = > > int op_open_agent(void); > Open an agent session. > void op_close_agent(void); > Close an agent session and free resources. > int op_write_native_code(char const * symbol_name, > const void* code_addr, > unsigned int size); > Write out symbol mapping and binary code. Thanks for making this simple. Integrating this into Mono will take much less than having a working oprofile with your patch on my workstation, which for now is stuck to an "oprofile-0.9.1-15" package. Just a few questions... > = Inlined methods = > > According to the JVMTI spec inlined methods for the „Compiled Method Load“ event > will be sent for each method with the identical address range. I think we'll never generate anything for inlined methods. I guess this will simply mean that no profiling information will be available for them (everything will be accounted to the "container" method), true? > Currently the JVM (IBM J9 JIT) is guaranteed not to reuse addresses space. Currently the same is true for Mono! Well, we don't even recompile methods for now :-( > = Source Line numbers = > > It would be nice to have source line numbers and filenames for the Java code, > like the debug information that is usable for C binaries. > => > That is not feasible yet, because the JVM/JIT does not provide this information. > Maybe we have this on other JITs? Well, we would happily make use of this feature if exported by libopagent... That said, thanks a lot. I have no say on the actual code of this patch, but I hope this functionality (JIT code support) will be merged soon! Ciao, Massi |
From: Jens W. <jen...@de...> - 2007-05-14 13:37:08
|
Massi, On Friday 11 May 2007 21:23, Massimiliano Mantione wrote: > > =3D Inlined methods =3D > >=20 > > According to the JVMTI spec inlined methods for the =E2=80=9ECompiled M= ethod Load=E2=80=9C event=20 > > will be sent for each method with the identical address range. >=20 > I think we'll never generate anything for inlined methods. > I guess this will simply mean that no profiling information will be > available for them (everything will be accounted to the "container" > method), true? That's the way it should be. The JVMTI specs states literally: "Note also that several methods may be inlined into a single address range,= and that=20 this event will be sent for each method." If that is true, then it is a bug in the spec, because how can an JVMTI age= nt find out what's the "container" method?! I need to take a closer look on th= e events I get. Maybe a "JVM guru" can elaborate on this. > > Currently the JVM (IBM J9 JIT) is guaranteed not to reuse addresses spa= ce. >=20 > Currently the same is true for Mono! > Well, we don't even recompile methods for now :-( Good to hear! If you ever reuse address space, there should be a parameter to switch it of again, for profiling. > > =3D Source Line numbers =3D > >=20 > > It would be nice to have source line numbers and filenames for the Java= code,=20 > > like the debug information that is usable for C binaries. > > =3D> > > That is not feasible yet, because the JVM/JIT does not provide this inf= ormation. > > Maybe we have this on other JITs? >=20 > Well, we would happily make use of this feature if exported by > libopagent... Can you deliver line numbers for code offsets or just for one=20 per symbol/method? > That said, thanks a lot. You are welcome! Best, Jens =2D-=20 Jens Wilke=20 Linux on System z - Application Development Tools=20 phone +49-(0)7031-16-3936 - tl *120-3936 - email jen...@de...=20 IBM Germany Lab, Schoenaicher Str. 220, 71032 Boeblingen=20 |
From: Maynard J. <may...@us...> - 2007-05-15 20:21:28
|
Jens Wilke wrote: >Hi, > >This is (another) try to address the profiling of jitted code generated by (J)VMs. >I tried to incorporate all the previous ideas posted on this list, especially the >work from Jason Yeh. The new implementation tries to be modular and extensible >and should be easy to integrate. The concept of libopagent is kept, but the ELF >files are generated for a complete vma region at the end of the profile run. > >The patches are against Oprofile-0.9.2, however the existing code is only touched >very lightly, so it should be easy to apply it to CVS HEAD or other versions. > > > Jens, Nice piece of work so far. I applied the relevant patches to a 0.9.2 oprofile src tree and compiled with no problems. Your jvmti example agent also compiled fine. I had a little trouble running it at first. I tried to start my Java app (with -agentlib:libjvmti_oprofile) before starting oprofile, and it failed because it couldn't write the jitdump file to /var/lib/oprofile/jitdump. Your introductory posting implied that the directory permissions issue was resolved, but looking at the code, I see you create the jitdump directory with world writable permissions during 'opcontrol --reset' or 'opcontrol --start'. So, once I ran 'opcontrol --reset' _first_, I was then able to start my Java app with the agentlib attached and successfully collect a profile. Cool. A recent contribution to OProfile added the concept of "session-dir" to specify an arbitrary location for the samples directory. So when forward porting these patches to current OProfile CVS, the hard-coded dependency on /var/lib/oprofile will need to be replaced with a variable. For the agentlib, there appears to be a means for passing options, but we should also consider honoring an OP_SESSION_DIR environment variable, not only for this purpose, but in general. One issue that may be problematic is your installation of the shared library, libopagent.so, and the corresponding header file, libopagent.h. There have been discussions in the past on the oprofile-list about providing users with an API, but it is an extra maintenance burden that should be avoided if possible. I outline a possible solution below. John Levon has indicated a willingness in the past to host agent code in the OProfile project (_John_, please correct me if I'm misquoting you or if you've changed your mind). This means, for example, the jvmti_oprofile agent could be included in the OProfile build, thus eliminating the need to export the libopagent API externally. As with other parts of OProfile, the libopagent code could be compiled into a static library, and then libjvmti_oprofile.so could be compiled against that static library. Of course, the end user still needs access to the libjvmti_oprofile shared library, so that library does need to be installed. However, the only API this library implements is the one declared in <java-1-5-0>/include/jvmti.h. Given that, would we need to version this library? Regards, -Maynard [snip] |
From: Jens W. <jen...@de...> - 2007-05-16 12:35:41
|
Maynard, Thanks for the feedback! On Tuesday 15 May 2007 22:21, Maynard Johnson wrote: > Nice piece of work so far. I applied the relevant patches to a 0.9.2 > oprofile src tree and compiled with no problems. Your jvmti example > agent also compiled fine. I had a little trouble running it at first. I > tried to start my Java app (with -agentlib:libjvmti_oprofile) before > starting oprofile, and it failed because it couldn't write the jitdump > file to /var/lib/oprofile/jitdump. Your introductory posting implied > that the directory permissions issue was resolved, but looking at the > code, I see you create the jitdump directory with world writable > permissions during 'opcontrol --reset' or 'opcontrol --start'. So, once > I ran 'opcontrol --reset' _first_, I was then able to start my Java app > with the agentlib attached and successfully collect a profile. Cool. Looking at the opcontrol code the usual directory creation is in do_setup, so I will do it here also. There is no directory creation of /var/lib/oprofile etc. at "make install" time. Maybe it makes sense to add a "opcontrol --setup" to the "make install" target, so its getting quite foolproof?! I will also put a proper warning in the libopagent code, if the directory is missing. > A recent contribution to OProfile added the concept of "session-dir" to > specify an arbitrary location for the samples directory. So when > forward porting these patches to current OProfile CVS, the hard-coded > dependency on /var/lib/oprofile will need to be replaced with a > variable. For the agentlib, there appears to be a means for passing > options, but we should also consider honoring an OP_SESSION_DIR > environment variable, not only for this purpose, but in general. No, the agent lib just writes to /var/lib/oprofile/jitdump directory. These files are just intermediate files that could also go to /tmp (which would be an alternative to discuss). The files will be removed automatically, so its good to have them in one directory. The final results, the generated ELF files, must go to the correct session dir, of course. > One issue that may be problematic is your installation of the shared > library, libopagent.so, and the corresponding header file, > libopagent.h. There have been discussions in the past on the > oprofile-list about providing users with an API, but it is an extra > maintenance burden that should be avoided if possible. I outline a > possible solution below. > > John Levon has indicated a willingness in the past to host agent code in > the OProfile project (_John_, please correct me if I'm misquoting you or > if you've changed your mind). This means, for example, the > jvmti_oprofile agent could be included in the OProfile build, thus > eliminating the need to export the libopagent API externally. As with > other parts of OProfile, the libopagent code could be compiled into a > static library, and then libjvmti_oprofile.so could be compiled against > that static library. > > Of course, the end user still needs access to the libjvmti_oprofile > shared library, so that library does need to be installed. However, the > only API this library implements is the one declared in > <java-1-5-0>/include/jvmti.h. Given that, would we need to version this > library? That won't work out because would need to provide a lib for each possible JVM/JDK (dot product: {IBM, SUN}, {V1.5, V1.6}, {32, 64 Bit}), and you would add these as a build dependency. Idea/Proposal: We will have/need several agent implementations for different JIT interface. For Java at least JVMTI and JVMPI and may be some others using special features of JIT implementations. We put this stuff in the directory "extra" and copy this tree to "/usr/share/oprofile/extra" on "make install". The "user" needs to compile the agent by itself for its particular JVM before use. Best, Jens -- Jens Wilke Linux on System z - Application Development Tools phone +49-(0)7031-16-3936 - tl *120-3936 - email jen...@de... IBM Germany Lab, Schoenaicher Str. 220, 71032 Boeblingen |
From: Maynard J. <may...@us...> - 2007-05-16 14:24:41
|
Jens Wilke wrote: > Maynard, > > Thanks for the feedback! > > On Tuesday 15 May 2007 22:21, Maynard Johnson wrote: > [snip] >>permissions during 'opcontrol --reset' or 'opcontrol --start'. So, once >>I ran 'opcontrol --reset' _first_, I was then able to start my Java app >>with the agentlib attached and successfully collect a profile. Cool. > > > Looking at the opcontrol code the usual directory creation is in do_setup, > so I will do it here also. There is no directory creation of /var/lib/oprofile etc. > at "make install" time. > > Maybe it makes sense to add a "opcontrol --setup" > to the "make install" target, so its getting quite foolproof?! I like the idea behind this of setting up the necessary directories at install time. But we would need to make sure this would work for an rpm install also. > > I will also put a proper warning in the libopagent code, if the directory > is missing. > > >>A recent contribution to OProfile added the concept of "session-dir" to >>specify an arbitrary location for the samples directory. So when >>forward porting these patches to current OProfile CVS, the hard-coded >>dependency on /var/lib/oprofile will need to be replaced with a >>variable. For the agentlib, there appears to be a means for passing >>options, but we should also consider honoring an OP_SESSION_DIR >>environment variable, not only for this purpose, but in general. > > > No, the agent lib just writes to /var/lib/oprofile/jitdump directory. These > files are just intermediate files that could also go to /tmp (which would be > an alternative to discuss). The files will be removed automatically, so its > good to have them in one directory. Ah yes, of course. > > The final results, the generated ELF files, must go to the correct session dir, > of course. > > >>One issue that may be problematic is your installation of the shared >>library, libopagent.so, and the corresponding header file, >>libopagent.h. There have been discussions in the past on the >>oprofile-list about providing users with an API, but it is an extra >>maintenance burden that should be avoided if possible. I outline a >>possible solution below. >> >>John Levon has indicated a willingness in the past to host agent code in >>the OProfile project (_John_, please correct me if I'm misquoting you or >>if you've changed your mind). This means, for example, the >>jvmti_oprofile agent could be included in the OProfile build, thus >>eliminating the need to export the libopagent API externally. As with >>other parts of OProfile, the libopagent code could be compiled into a >>static library, and then libjvmti_oprofile.so could be compiled against >>that static library. >> >>Of course, the end user still needs access to the libjvmti_oprofile >>shared library, so that library does need to be installed. However, the >>only API this library implements is the one declared in >><java-1-5-0>/include/jvmti.h. Given that, would we need to version this >>library? > > > That won't work out because would need to provide a lib for each > possible JVM/JDK (dot product: {IBM, SUN}, {V1.5, V1.6}, {32, 64 Bit}), and you > would add these as a build dependency. Right, we wouldn't want to try to build everything by default. But how about adding some new configure options (for locations of <vm>/include directories) and some specific make targets to create the individual agent libraries? A couple of downsides to this idea. One is that it puts the burden on the distros to make an agent library for every type of VM the distro would include in their install. Another is that a user may wish to add a new VM (supported by OProfile) to a system post-install, but they would have no easy way of compiling the necessary agent library. -Maynard > > Idea/Proposal: > > We will have/need several agent implementations for different JIT interface. > For Java at least JVMTI and JVMPI and may be some others using special > features of JIT implementations. > > We put this stuff in the directory "extra" and copy this tree to > "/usr/share/oprofile/extra" on "make install". The "user" needs to > compile the agent by itself for its particular JVM before use. > > Best, > > Jens > |
From: John L. <le...@mo...> - 2007-05-19 14:20:10
|
On Tue, May 15, 2007 at 03:21:19PM -0500, Maynard Johnson wrote: > file to /var/lib/oprofile/jitdump. Your introductory posting implied > that the directory permissions issue was resolved, but looking at the > code, I see you create the jitdump directory with world writable > permissions during 'opcontrol --reset' or 'opcontrol --start'. So, once > I ran 'opcontrol --reset' _first_, I was then able to start my Java app > with the agentlib attached and successfully collect a profile. Cool. Hmm. So I'm a little confused about the situation now. We have a world-writable directory for the temporary JIT files that everybody's Java instance writes into? Presumably this directory is created with /tmp/ style permissions so users cannot interfere with other user's JIT files? > John Levon has indicated a willingness in the past to host agent code in > the OProfile project (_John_, please correct me if I'm misquoting you or > if you've changed your mind). This means, for example, the > jvmti_oprofile agent could be included in the OProfile build, thus > eliminating the need to export the libopagent API externally. As with > other parts of OProfile, the libopagent code could be compiled into a > static library, and then libjvmti_oprofile.so could be compiled against > that static library. It's important that we ship Java support out of the box. For now, we probably don't need to worry too much about providing an API for the agents, but later on we definitely do want to do this. Whilst things are still being hashed out, we can keep it private. Talking of Java, has this been tested with Sun's JVM? regards john |
From: Jens W. <jen...@de...> - 2007-05-22 15:12:53
|
On Saturday 19 May 2007 16:25, John Levon wrote: > On Tue, May 15, 2007 at 03:21:19PM -0500, Maynard Johnson wrote: > > > file to /var/lib/oprofile/jitdump. Your introductory posting implied > > that the directory permissions issue was resolved, but looking at the > > code, I see you create the jitdump directory with world writable > > permissions during 'opcontrol --reset' or 'opcontrol --start'. So, once > > I ran 'opcontrol --reset' _first_, I was then able to start my Java app > > with the agentlib attached and successfully collect a profile. Cool. > > Hmm. So I'm a little confused about the situation now. We have a > world-writable directory for the temporary JIT files that everybody's > Java instance writes into? Presumably this directory is created with > /tmp/ style permissions so users cannot interfere with other user's JIT > files? Yes, that's the idea behind it, allthough I have not yet set the file permissions correctly in libopagent. > It's important that we ship Java support out of the box. For now, we > probably don't need to worry too much about providing an API for the > agents, but later on we definitely do want to do this. Whilst things are > still being hashed out, we can keep it private. > Talking of Java, has this been tested with Sun's JVM? Yes, it works also with the Sun JVM. I am not sure whether we can rely on that a JVMTI agent compiled against the Sun headers will run with the IBM JVM or vice versa. So, nobody states that a compiled JVMTI agent will be working with any JVM. I did a quick test and compiled with the IBM headers and it works also on a Sun JVM, but, I would not count on this being always the case for the future. Maybe someone else can tell us more here. That's why I though we keep the agent implementation separate and let the user compile it for the right JVM itself. Best, Jens -- Jens Wilke Linux on System z - Application Development Tools phone +49-(0)7031-16-3936 - tl *120-3936 - email jen...@de... IBM Germany Lab, Schoenaicher Str. 220, 71032 Boeblingen |
From: John L. <le...@mo...> - 2007-05-19 20:25:28
|
On Fri, May 11, 2007 at 07:30:21PM +0200, Jens Wilke wrote: > - The mechanism must require only minimum changes to the current > Oprofile infrastructure [JY] An unusual requirement. It would be really useful for me if you could give a full example from the JIT dump file to the output of opreport etc. > On behalf of the event the method binary code, symbol name and starting address > will be written sequencially to a file. The agent implementation does not This also includes size I think? > The rationale behind the dump command is (thats a guess maybe someone can detail > on this?) to have already valid data in the filesystem to run the analysis tools > already during the profiling session. It's designed to be used when profiling low-frequency events that may not have generated enough events to fill the kernel buffers and wake up the daemon. > With the enhancement we want to support dump and stop correctly, meaning that the > ELF files will be generated when a dump command is issued with the data that is > currently available. On stop the ELF files will also be generated. If there are > existing files from a previous dump these files will be overwritten. Stop or shutdown? They are very different operations. > The generation of the ELF files works this way: Have you considered a periodic creation/update of the ELF files too? How would I know, as a user of 'opreport', that my profile data is not quite up to date? > Before usage a VM must call the op_open_agent function. The file <pid>.dump > will be created. Each call to op_write_native_code writes out the information to > this file. What happens on PID reuse? > = Header = > > The file has an ASCII header, so the header is readable and extensible. > It is read line by line. Each line has the format "<Param>: <Value>". > The end of the header is marked by an empty line (just as RFC822). > > Here is an example: > > ---snip--- > Oprofile Agent JIT-Dump > Version: 1 > BFD-Target: elf32-i386 > BFD-Arch: 8 > BFD-Mach: 1 > BFD-Printable-Name: i386 > > ---snip--- > > The sole purpose of the header information, right now, is to transport the > machine archtecture of the code that the file contains. This is important > on 64 bit machines if we run 32/64 bit mixed VMs. I'm confused about why this is readable ASCII rather than a simple 'machine' format. > After the header the file contains records of the following format: > > <type> <name> <description> > char record_id ID of the record (always 0) > uint32 record_size total size of this record, not including record_id and record_size fields > char[] symbol_name symbol name of the code as 0 terminated string > uint64 code_addr start address in memory of the code > uint32 code_size size of the code > char[] code the code itself > > The idea of the record_id field is to be extensible. Most likely we may want > to transport additional information (see issues). The record_size is used to check > whether the record is complete before it is parsed. It may happen that the JIT-Dump > file still gets written to, while we generate the ELF file. It sounds like this file is always append-only, right? That's a nice property. > Oprofile shows in the reports the image name in a separate column. > Showing the region data (PID, start- and end-address) as it is now, is not useful > for the user in the general case. > > Whats useful for a Java method? The defining class? The JAR-file? The data > from java.lang.Package.getImplementationVendor() or > java.lang.Package.getImplementationTitle() would be useful candidates. > > To show customized output in the image name column we need to do changes to > the analysis tools and find a way to transport the information. Do you have a plan for this? > = Missing anonymous samples = > > When processing the sample data the Oprofile daemon gets additional information > for each process from /proc/<pid>/maps. There may be a delay between the sample > event and the processing, because of the fact that the sample data gets buffered. > Short running processes may be already terminated when the Oprofile daemon tries > to access this data. In this case the anonymous samples will not be stored. > > A temporary workaround is to raise the buffer-watershed if short running > programs get profiled. Right, this is a generic and unpleasant issue. > = Removal of JIT-dump files = > > When should the JIT-dump files be removed? > => > The jitdump directory is cleared on opcontrol --reset. On opcontrol --start > JIT-Dump files get removed, that are not open. Why isn't it cleared when we do the conversion from the JIT dump files to the ELF files? > = ELF filenames = > > Naming and position of the generated ELF files must be carefully considered. > Impact on Oprofile add-on tools should be minimized. > => > John Levon proposes to put the ELF files to /var/lib/oprofile/samples/current/{anon}/, > see [JL0502] > The naming is identical to the directory following {anon}, > e.g. 1720.0x200002ba000.0x200002c6000. So where do they end up? > binutils and ELF also supports UTF-8 strings. Oprofile should do this correctly. > (needs testing) Hmm. Not sure about this. What about XML output (and also do we need extensions there too for the JIT output?) > It cannot be expected that the JVM runs under root privileges. The JIT-dump is > written within the JVM process context. The directory /var/lib/oprofile > is not world writable. > => > /var/lib/oprofile/jitdump needs to have the correct permissions. Note the implication here is that the code that reads in the JIT dump files must be entirely robust to a malicious user. I presume that part of the code has been, or will be, audited as such? > Currently the JVM (IBM J9 JIT) is guaranteed not to reuse addresses space. What about Sun JVM? > = Library interface or sockets or file interface = > > The library libopagent must be provided for 31/32 and 64 bit on 64 bit platforms > (multilib). Providing multiple libraries is different among distributions, so it > is up to the distributors packager to properly provide both library versions. Is it really different? What differences? regards john |
From: John L. <le...@mo...> - 2007-05-19 20:33:35
|
--- oprofile-0.9.2/libopagent/bfddefines.c 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/libopagent/bfddefines.c 2007-05-11 15:55:47.000000000 +0200 @@ -0,0 +1,40 @@ +/** + * @file bfddefines.c + * Write out defines for BFD arch and mach of the given binary Hmm, is there really no better way to get the values needed here? Since this is a build tool we might want to put it in a separate directory, perhaps. +int op_open_agent() { Functions are like: int op_open_agent() { + char tmp[PATH_MAX + 1]; + char *str; char * str; etc. + snprintf(tmp, PATH_MAX, "%s/%i.dump", AGENT_DIR, getpid()); + dumpfile = fopen(tmp, "w"); + if (dumpfile==NULL) { dumpfile == NULL and general coding style elsewhere. Can we open it append-only~? + if (dumpfile==NULL) { + fprintf(stderr, "LIBOPAGENT ERROR: op_open_agent must be called first\n"); Let's not get over-excited. We don't need a message for a simple programming error. + uint32_t code_size = size; + uint64_t addr = (uint64_t) (unsigned int) code; This can't be right for 64-bit JVMs? + /* DEBUG + fprintf(stderr, "op_write_native_code: %s, code_addr=%llx, code_size=%i\n", symbol_name, addr, size); + */ Get rid of such before integration. --- oprofile-0.9.2/libopagent/libopagent.h 1970-01-01 01:00:00.000000000 +0100 +++ oprofile-jitadaptions/libopagent/libopagent.h 2007-05-04 15:01:34.000000000 +0200 @@ -0,0 +1,19 @@ +#ifndef _LIB_OPAGENT_H +#define _LIB_OPAGENT_H + +#include <sys/types.h> + +int op_open_agent(); + +void op_close_agent(); + +int op_write_native_code( + char const * symbol_name, + const void * code, + const unsigned int code_size); This all needs clear docs. Ideally a chapter in the Internals manual too. We absolutely need to consider further extensions like the line number stuff, although we can probably keep this as-is for now since we're not going to make it public yet. +# FIXME: automake? Yes. +# FIXME: versioning of library? For now we're keeping it private. regards john |
From: Jens W. <jen...@de...> - 2007-05-22 15:13:05
|
On Saturday 19 May 2007 22:38, John Levon wrote: > + * @file bfddefines.c > + * Write out defines for BFD arch and mach of the given binary > > Hmm, is there really no better way to get the values needed here? Not sure, but that's the best I've found yet. > + snprintf(tmp, PATH_MAX, "%s/%i.dump", AGENT_DIR, getpid()); > + dumpfile = fopen(tmp, "w"); > + if (dumpfile==NULL) { > Can we open it append-only~? Yes, will change. > dumpfile == NULL > > and general coding style elsewhere. Ok. will change. > + if (dumpfile==NULL) { > + fprintf(stderr, "LIBOPAGENT ERROR: op_open_agent must be called first\n"); > > Let's not get over-excited. We don't need a message for a simple > programming error. Ok. > > + uint32_t code_size = size; > + uint64_t addr = (uint64_t) (unsigned int) code; > > This can't be right for 64-bit JVMs? Oops, yes, that's a bug! > + /* DEBUG > + fprintf(stderr, "op_write_native_code: %s, code_addr=%llx, code_size=%i\n", symbol_name, addr, size); > + */ > > Get rid of such before integration. Everything tagged with DEBUG and FIXME will go away when I post something for intergration, sure. onst unsigned int code_size); > > This all needs clear docs. Ideally a chapter in the Internals manual > too. We absolutely need to consider further extensions like the line > number stuff, although we can probably keep this as-is for now since > we're not going to make it public yet. It's a good idea to add something to the docs. If we have something for integration I will prepare that. Best, Jens -- Jens Wilke Linux on System z - Application Development Tools phone +49-(0)7031-16-3936 - tl *120-3936 - email jen...@de... IBM Germany Lab, Schoenaicher Str. 220, 71032 Boeblingen |
From: John L. <le...@mo...> - 2007-05-20 18:01:23
|
On Fri, May 11, 2007 at 07:42:54PM +0200, Jens Wilke wrote: @@ -1216,9 +1218,31 @@ do_start_daemon() echo "Daemon started." } +# create jitdump directory and remove any old files from +# a previous run +prep_jitdump() { + local dumpdir=$DIR/jitdump Note the jit dump file must be local to the sample session (i.e. under current). /var/lib/oprofile/samples/current/{jit}/ ? + local files=`find /proc/$pid/fd -lname $I`; I'm not sure "local" is portable. opcontrol is supposed to work with various shells i.e. be as portable as possible. +do_jit2elf() { + OPJIT2ELF="$OPDIR/opjit2elf" + DEST=$SAMPLES_DIR/current/{anon} + JITDIR=$DIR/jitdump; + create_dir $DEST; + + # this gives us a list of the regions with anon samples, e.g.: + # 7189.0xa4741000.0xa47c1000 7189.0xa53f2000.0xa5472000 11835.0x96d74000.0x96df4000 + local anonlist="`find $SAMPLES_DIR/current -path \*{anon}/\* -type d -printf '%f '`" + test -n "$anonlist" && for I in $anonlist; do + local pid=`expr $I : '\(.*\)\..*\.`; + local start=`expr $I : '.*\.0x\(.*\)\.'`; + local end=`expr $I : '.*\..*\.0x\(.*\)'`; + local jitdump=$JITDIR/$pid.dump + if test -f $jitdump; then + echo found jitdump $jitdump, extracting region $start-$end + $OPJIT2ELF $jitdump $DEST/$I $start $end + fi + done + # FIXME: we may run prep_jitdump here to clean the directory but let's keep them + # at the moment (for debugging...) +} Note that current CVS creates files like: /var/lib/oprofile/samples/current/{root}/home/moz/local/bin/32mplayer/{dep}/{anon:anon}/15121.0xffffe000.0xfffff000/CPU_CLK_UNHALTED.100000.0.all.all.all so you'll need to fix this. And make sure you handle the {anon:anon} bit right, as that can be different. I think I'd prefer a directory for the JIT dump pid ie. $JITDIR/$pid/. Indeed I wonder if we could keep the temporary and the permanent files in the same place - would this let a normal user create their own ELF files if desired? regards john |
From: John L. <le...@mo...> - 2007-05-20 18:03:30
|
On Fri, May 11, 2007 at 07:43:29PM +0200, Jens Wilke wrote: > /** > * @param component path component > + * @param basse_dir is filled with the base directory with a trailing and leading / 'base' > * > * remove from path_component all directory left to {root}, {kern} or {anon} > */ > -void remove_base_dir(vector<string> & path) > +void remove_base_dir(vector<string> & path, string & base_dir) > { > vector<string>::iterator it; > + base_dir = "/"; > for (it = path.begin(); it != path.end(); ++it) { > if (*it == "{root}" || *it == "{kern}" || *it == "{anon}") > break; > + base_dir += *it+"/"; > } Don't understand this. We're passing in and modifying the 'base_dir' each time?? > path.erase(path.begin(), it); > @@ -109,6 +112,7 @@ parsed_filename parse_filename(string co > } > string event_spec = filename.substr(pos + 1); > string filename_spec = filename.substr(0, pos); > + string base_dir = ""; > > parsed_filename result = parse_event_spec(event_spec); > > @@ -116,7 +120,7 @@ parsed_filename parse_filename(string co > > vector<string> path = separate_token(filename_spec, '/'); > > - remove_base_dir(path); > + remove_base_dir(path, base_dir); > > // pp_interface PP:3.19 to PP:3.23 path must start either with {root} > // or {kern} and we must found at least 2 component, remove_base_dir() > @@ -158,7 +162,9 @@ parsed_filename parse_filename(string co > break; > > if (anon) { > - result.lib_image = parse_anon(path[i++]); > + // result.lib_image = parse_anon(path[i++]); > + // point to a (potentially) generated elf file from opjit2elf > + result.lib_image = base_dir + "{anon}/"+path[i++]; Does this mean parse_anon is no longer used? I'm not sure I really understand exactly what this patch is doing. It needs merging with the VDSO patches in CVS I think. regards john |
From: Jens W. <jen...@de...> - 2007-05-22 15:12:56
|
On Sunday 20 May 2007 20:08, John Levon wrote: > Does this mean parse_anon is no longer used? I'm not sure I really > understand exactly what this patch is doing. I am sorry. It works, but this one is definitly still a hack and I need some directions here. parse_anon is changed to produce a file name for the ELF binary (right now identlical to the sample filename). Then I used remove_base_dir to extract the base from the complete filename, maybe the base is acessible somewhere in the context directly. But before going into detail here, the question is, whether this is the right place to do the change. Should we change here, or do it somewhere when the file gets opened? Changing parse_anon would also mean, that we change the displayed name for the annonymous regions, regardless whether there are ELF files or not. Best, Jens -- Jens Wilke Linux on System z - Application Development Tools phone +49-(0)7031-16-3936 - tl *120-3936 - email jen...@de... IBM Germany Lab, Schoenaicher Str. 220, 71032 Boeblingen |
From: John L. <le...@mo...> - 2007-05-20 18:06:53
|
On Fri, May 11, 2007 at 07:44:34PM +0200, Jens Wilke wrote: > This patch contains the example JVMTI implementation. We shouldn't have an example, we should have a working reference implementation. So: agents/jvmti/ for example. And it should use standard automake etc. infrastructure like the other bits. regards john > + const jvmtiAddrLocationMap* map, > + const void* compile_info) > +{ > + /* Get the declaring class of the method */ > + jclass declaringClass; > + char * classSig = NULL; > + char * methodName = NULL; > + char * methodSig = NULL; > + jvmtiError err; Do you have a reason not to use standard oprofile formatting and style? regards john |
From: Maynard J. <may...@us...> - 2007-05-22 20:23:43
|
John Levon wrote: > On Fri, May 11, 2007 at 07:44:34PM +0200, Jens Wilke wrote: > > >>This patch contains the example JVMTI implementation. > > > We shouldn't have an example, we should have a working reference > implementation. So: > > agents/jvmti/ > > for example. And it should use standard automake etc. infrastructure > like the other bits. Yes, I agree. To facilitate building the Java agent "out of the box", I think we need some new configure options for the JDK path and "bitness". If/when more agents are integrated, we would need more configure options specific to those agent types. -Maynard > > regards > john > > >>+ const jvmtiAddrLocationMap* map, >>+ const void* compile_info) >>+{ >>+ /* Get the declaring class of the method */ >>+ jclass declaringClass; >>+ char * classSig = NULL; >>+ char * methodName = NULL; >>+ char * methodSig = NULL; >>+ jvmtiError err; > > > Do you have a reason not to use standard oprofile formatting and style? > > regards > john > > ------------------------------------------------------------------------- > This SF.net email is sponsored by DB2 Express > Download DB2 Express C - the FREE version of DB2 express and take > control of your XML. No limits. Just data. Click to get it now. > http://sourceforge.net/powerbar/db2/ > _______________________________________________ > oprofile-list mailing list > opr...@li... > https://lists.sourceforge.net/lists/listinfo/oprofile-list |
From: John L. <le...@mo...> - 2007-05-20 18:08:02
|
On Fri, May 11, 2007 at 07:34:27PM +0200, Jens Wilke wrote: > This patch adds the program opjit2elf which converts a JIT-Dump file > to an ELF file. Whole program needs formatting for oprofile style and the audit I mentioned earlier. I wonder if we can't think of a less awkward name for the binary? regards john |