使用JVM TI技术插装字节码,实现Java代码执行控监Agent

        JVM TI技术是JAVA5以后的版本推出的技术,即JVM编程接口,该技术广泛应用于各种开发工具,例如Eclipse等。使用JVM TI可以开发JAVA调试工具,JAVA代码执行监控工具等。同时,了解JVM TI技术也有助于JAVA程序员深入了解JVM的原理,上一篇文章我介绍了采用JVM TI的事件通知技术开发JVM监控工具,但是使用事件通知技术开发应程序性能是非常差劲的,对于源码较多的工程,使用事件通知来捕获JAVA的执行过程,是不可取的,将会对程序的性能造成严重的影响。

        在JAVA语言中,可以使用AOP技术修改字节码,著名的框架有Spring,Guice等框架,可以根据程序员的配置对指定的类进行字节码插装,但该技术取决程序员的主观动态,适合在程序的开发过程中使用,如果要实现黑盒的监控过程,是不适用的。例如想知道程序执行过程中调用了哪些类,哪些方法,想通过捕获JAVA调用栈了解程序的执行性能等等,使用JVM TI技术是适用的,商业工具JTest就是采用JVM TI技术实现的,再如QTP等自动化测试工具对JAVA SE程序的界面识别,也是采用JVM TI技术实现。总而言之,采用JVM TI技术,我们可以实现如下功能:

  1.         了解程序的执行过程
  2.         监控程序的执行性能
  3.         调试你的多线程程序
  4.         开发JAVA的自动化测试工具
  5.         开发JAVA的单元测试工具

       第一步: 本程序我使用VS 2008工具开发和编译,首先使用VS 2008创建一个Win32本地应用程序,详细的步骤请大家参考其他专业文章。

       第二步:我们需要使用几个JVM的API类,分别是:agent_util,java_crw_demo两个API,这两个api我们可以在JAVA的例子中找到,其中java_crw_demo是用来插装字节码的,agent_util是agent的工具类

     1、agent_util.cpp

/*
 * 
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 * 
 * -Redistribution in binary form must reproduce the above copyright notice, 
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may 
 * be used to endorse or promote products derived from this software without 
 * specific prior written permission.
 * 
 * This software is provided "AS IS," without a warranty of any kind. ALL 
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST 
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, 
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY 
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, 
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * 
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <agent_util.h>

/* ------------------------------------------------------------------- */
/* Generic C utility functions */

/* Send message to stdout or whatever the data output location is */
void stdout_message(const char * format, ...)
{
    va_list ap;

    va_start(ap, format);
    (void)vfprintf(stdout, format, ap);
    va_end(ap);
}

/* Send message to stderr or whatever the error output location is and exit  */
void fatal_error(const char * format, ...)
{
    va_list ap;

    va_start(ap, format);
    (void)vfprintf(stderr, format, ap);
    (void)fflush(stderr);
    va_end(ap);
    exit(3);
}

/* Get a token from a string (strtok is not MT-safe)
 *    str	String to scan
 *    seps      Separation characters
 *    buf       Place to put results
 *    max       Size of buf
 *  Returns NULL if no token available or can't do the scan.
 */
char * get_token(char *str, char *seps, char *buf, int max)
{
    int len;
    
    buf[0] = 0;
    if ( str==NULL || str[0]==0 ) {
	return NULL;
    }
    str += strspn(str, seps);
    if ( str[0]==0 ) {
	return NULL;
    }
    len = (int)strcspn(str, seps);
    if ( len >= max ) {
	return NULL;
    }
    (void)strncpy(buf, str, len);
    buf[len] = 0;
    return str+len;
}

/* Determines if a class/method is specified by a list item 
 *   item	String that represents a pattern to match
 *          	  If it starts with a '*', then any class is allowed
 *                If it ends with a '*', then any method is allowed
 *   cname	Class name, e.g. "java.lang.Object"
 *   mname      Method name, e.g. "<init>"
 *  Returns 1(true) or 0(false).
 */
static int covered_by_list_item(char *item, char *cname, char *mname)
{
    int      len;
    
    len = (int)strlen(item);
    if ( item[0]=='*' ) {
	if ( strncmp(mname, item+1, len-1)==0 ) {
	    return 1;
	}
    } else if ( item[len-1]=='*' ) {
	if ( strncmp(cname, item, len-1)==0 ) {
	    return 1;
	}
    } else {
	int cname_len;
	
	cname_len = (int)strlen(cname);
	if ( strncmp(cname, item, (len>cname_len?cname_len:len))==0 ) {
	    if ( cname_len >= len ) {
		/* No method name supplied in item, we must have matched */
		return 1;
	    } else {
		int mname_len;
		
		mname_len = (int)strlen(mname);
		item += cname_len+1;
		len -= cname_len+1;
		if ( strncmp(mname, item, (len>mname_len?mname_len:len))==0 ) {
		    return 1;
		}
	    }
	}
    }
    return 0;
}

/* Determines if a class/method is specified by this list
 *   list	String of comma separated pattern items
 *   cname	Class name, e.g. "java.lang.Object"
 *   mname      Method name, e.g. "<init>"
 *  Returns 1(true) or 0(false).
 */
static int covered_by_list(char *list, char *cname, char *mname)
{
    char  token[1024];
    char *next;
    
    if ( list[0] == 0 ) {
        return 0;
    }
	
    next = get_token(list, ",", token, sizeof(token));
    while ( next != NULL ) {
	if ( covered_by_list_item(token, cname, mname) ) {
	    return 1;
	}
        next = get_token(next, ",", token, sizeof(token));
    }
    return 0;
}

/* Determines which class and methods we are interested in 
 *   cname		Class name, e.g. "java.lang.Object"
 *   mname      	Method name, e.g. "<init>"
 *   include_list	Empty or an explicit list for inclusion
 *   exclude_list	Empty or an explicit list for exclusion
 *  Returns 1(true) or 0(false).
 */
int interested(char *cname, char *mname, char *include_list, char *exclude_list)
{
    if ( exclude_list!=NULL && exclude_list[0]!=0 && 
	    covered_by_list(exclude_list, cname, mname) ) {
        return 0;
    }
    if ( include_list!=NULL && include_list[0]!=0 && 
	    !covered_by_list(include_list, cname, mname) ) {
        return 0;
    }
    return 1;
}

/* ------------------------------------------------------------------- */
/* Generic JVMTI utility functions */

/* Every JVMTI interface returns an error code, which should be checked
 *   to avoid any cascading errors down the line.
 *   The interface GetErrorName() returns the actual enumeration constant
 *   name, making the error messages much easier to understand.
 */
void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError errnum, const char *str)
{
    if ( errnum != JVMTI_ERROR_NONE ) {
	char       *errnum_str;
	
	errnum_str = NULL;
	(void)(jvmti)->GetErrorName(errnum, &errnum_str);
	
	fatal_error("ERROR: JVMTI: %d(%s): %s\n", errnum, 
		(errnum_str==NULL?"Unknown":errnum_str),
		(str==NULL?"":str));
    }
}

/* All memory allocated by JVMTI must be freed by the JVMTI Deallocate
 *   interface.
 */
void deallocate1(jvmtiEnv *jvmti, char *ptr)
{
    jvmtiError error;
    error = (jvmti)->Deallocate((unsigned char*)ptr);
    check_jvmti_error(jvmti, error, "Cannot deallocate memory");
}

/* Allocation of JVMTI managed memory */
void * allocate(jvmtiEnv *jvmti, jint len)
{
    jvmtiError error;
    void      *ptr;
    
    error = (jvmti)->Allocate(len, (unsigned char **)&ptr);
    check_jvmti_error(jvmti, error, "Cannot allocate memory");
    return ptr;
}

/* Add demo jar file to boot class path (the BCI Tracker class must be 
 *     in the boot classpath)
 *
 *   WARNING: This code assumes that the jar file can be found at one of:
 *              ${JAVA_HOME}/demo/jvmti/${DEMO_NAME}/${DEMO_NAME}.jar
 *              ${JAVA_HOME}/../demo/jvmti/${DEMO_NAME}/${DEMO_NAME}.jar
 *            where JAVA_HOME may refer to the jre directory.
 *            Both these values are added to the boot classpath.
 *            These locations are only true for these demos, installed
 *            in the JDK area. Platform specific code could be used to
 *            find the location of the DLL or .so library, and construct a
 *            path name to the jar file, relative to the library location.
 */
void
add_demo_jar_to_bootclasspath(jvmtiEnv *jvmti, char *demo_name)
{
    jvmtiError error;
    char      *file_sep;
    int        max_len;
    char      *java_home;
    char       jar_path[FILENAME_MAX+1];
   
    java_home = NULL;
    error = (jvmti)->GetSystemProperty("java.home", &java_home);
    check_jvmti_error(jvmti, error, "Cannot get java.home property value");
    if ( java_home == NULL || java_home[0] == 0 ) {
	fatal_error("ERROR: Java home not found\n");
    }
   
#ifdef WIN32
    file_sep = "\\";
#else
    file_sep = "/";
#endif
    
    max_len = (int)(strlen(java_home) + strlen(demo_name)*2 + 
			 strlen(file_sep)*5 +16 /* ".." "demo" "jvmti" ".jar" NULL */ );
    if ( max_len > (int)sizeof(jar_path) ) {
	fatal_error("ERROR: Path to jar file too long\n");
    }
    (void)strcpy(jar_path, java_home);
    (void)strcat(jar_path, file_sep);
    (void)strcat(jar_path, "demo");
    (void)strcat(jar_path, file_sep);
    (void)strcat(jar_path, "jvmti");
    (void)strcat(jar_path, file_sep);
    (void)strcat(jar_path, demo_name);
    (void)strcat(jar_path, file_sep);
    (void)strcat(jar_path, demo_name);
    (void)strcat(jar_path, ".jar");
    error = (jvmti)->AddToBootstrapClassLoaderSearch((const char*)jar_path);
    check_jvmti_error(jvmti, error, "Cannot add to boot classpath");
    
    (void)strcpy(jar_path, java_home);
    (void)strcat(jar_path, file_sep);
    (void)strcat(jar_path, "..");
    (void)strcat(jar_path, file_sep);
    (void)strcat(jar_path, "demo");
    (void)strcat(jar_path, file_sep);
    (void)strcat(jar_path, "jvmti");
    (void)strcat(jar_path, file_sep);
    (void)strcat(jar_path, demo_name);
    (void)strcat(jar_path, file_sep);
    (void)strcat(jar_path, demo_name);
    (void)strcat(jar_path, ".jar");
    
    error = (jvmti)->AddToBootstrapClassLoaderSearch((const char*)jar_path);
    check_jvmti_error(jvmti, error, "Cannot add to boot classpath");
}

/* ------------------------------------------------------------------- */

 2、java_crw_demo.cpp

/*
 * @(#)java_crw_demo.c	1.39 06/01/27
 * 
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 * 
 * -Redistribution in binary form must reproduce the above copyright notice, 
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may 
 * be used to endorse or promote products derived from this software without 
 * specific prior written permission.
 * 
 * This software is provided "AS IS," without a warranty of any kind. ALL 
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST 
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, 
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY 
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, 
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * 
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */

/* Class reader writer (java_crw_demo) for instrumenting bytecodes */

/*
 * As long as the callbacks allow for it and the class number is unique,
 *     this code is completely re-entrant and any number of classfile 
 *     injections can happen at the same time.
 *
 *     The current logic requires a unique number for this class instance
 *     or (jclass,jobject loader) pair, this is done via the ClassIndex
 *     in hprof, which is passed in as the 'unsigned cnum' to java_crw_demo().
 *     It's up to the user of this interface if it wants to use this
 *     feature.
 *
 * Example Usage: See file test_crw.c.
 *
 */
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <java_crw_demo.h>
/* Get Java and class file and bytecode information. */

#include <jni.h>

#include "classfile_constants.h"


/* Include our own interface for cross check */


/* Macros over error functions to capture line numbers */

#define CRW_FATAL(ci, message) fatal_error(ci, message, __FILE__, __LINE__)

#if defined(DEBUG) || !defined(NDEBUG)

  #define CRW_ASSERT(ci, cond) \
        ((cond)?(void)0:assert_error(ci, #cond, __FILE__, __LINE__))
  
#else
  
  #define CRW_ASSERT(ci, cond)
  
#endif

#define CRW_ASSERT_MI(mi) CRW_ASSERT((mi)?(mi)->ci:NULL,(mi)!=NULL)

#define CRW_ASSERT_CI(ci) CRW_ASSERT(ci, ( (ci) != NULL && \
                         (ci)->input_position <= (ci)->input_len && \
                         (ci)->output_position <= (ci)->output_len) )

/* Typedefs for various integral numbers, just for code clarity */

typedef unsigned       ClassOpcode;             /* One opcode */
typedef unsigned char  ByteCode;                /* One byte from bytecodes */
typedef int            ByteOffset;              /* Byte offset */
typedef int            ClassConstant;           /* Constant pool kind */
typedef long           CrwPosition;             /* Position in class image */
typedef unsigned short CrwCpoolIndex;           /* Index into constant pool */

/* Misc support macros */

/* Given the position of an opcode, find the next 4byte boundary position */
#define NEXT_4BYTE_BOUNDARY(opcode_pos) (((opcode_pos)+4) & (~3))

#define LARGEST_INJECTION               (12*3) /* 3 injections at same site */
#define MAXIMUM_NEW_CPOOL_ENTRIES       64 /* don't add more than 32 entries */

/* Constant Pool Entry (internal table that mirrors pool in file image) */

typedef struct {
    const char *        ptr;            /* Pointer to any string */
    unsigned short      len;            /* Length of string */
    unsigned int        index1;         /* 1st 16 bit index or 32bit value. */
    unsigned int        index2;         /* 2nd 16 bit index or 32bit value. */
    ClassConstant       tag;            /* Tag or kind of entry. */
} CrwConstantPoolEntry;

struct MethodImage;

/* Class file image storage structure */

typedef struct CrwClassImage {
    
    /* Unique class number for this class */
    unsigned                    number;
   
    /* Name of class, given or gotten out of class image */
    const char *                name;
   
    /* Input and Output class images tracking */
    const unsigned char *       input;
    unsigned char *             output;
    CrwPosition                 input_len;
    CrwPosition                 output_len;
    CrwPosition                 input_position;
    CrwPosition                 output_position;
   
    /* Mirrored constant pool */
    CrwConstantPoolEntry *      cpool;
    CrwCpoolIndex               cpool_max_elements;             /* Max count */
    CrwCpoolIndex               cpool_count_plus_one;
   
    /* Input flags about class (e.g. is it a system class) */
    int                         system_class;

    /* Class access flags gotten from file. */
    unsigned                    access_flags;
   
    /* Names of classes and methods. */
    char* tclass_name;          /* Name of class that has tracker methods. */
    char* tclass_sig;           /* Signature of class */
    char* call_name;            /* Method name to call at offset 0 */
    char* call_sig;             /* Signature of this method */
    char* return_name;          /* Method name to call before any return */
    char* return_sig;           /* Signature of this method */
    char* obj_init_name;        /* Method name to call in Object <init> */
    char* obj_init_sig;         /* Signature of this method */
    char* newarray_name;        /* Method name to call after newarray opcodes */
    char* newarray_sig;         /* Signature of this method */

    /* Constant pool index values for new entries */
    CrwCpoolIndex               tracker_class_index;
    CrwCpoolIndex               object_init_tracker_index;
    CrwCpoolIndex               newarray_tracker_index;
    CrwCpoolIndex               call_tracker_index;
    CrwCpoolIndex               return_tracker_index;
    CrwCpoolIndex               class_number_index; /* Class number in pool */
    
    /* Count of injections made into this class */
    int                         injection_count;

    /* This class must be the java.lang.Object class */
    jboolean                    is_object_class;
    
    /* This class must be the java.lang.Thread class */
    jboolean                    is_thread_class;
    
    /* Callback functions */
    FatalErrorHandler           fatal_error_handler;
    MethodNumberRegister        mnum_callback;

    /* Table of method names and descr's */
    int                         method_count;
    const char **               method_name;
    const char **               method_descr;
    struct MethodImage *        current_mi;

} CrwClassImage;

/* Injection bytecodes (holds injected bytecodes for each code position) */

typedef struct {
    ByteCode *  code;
    ByteOffset  len;
} Injection;

/* Method transformation data (allocated/freed as each method is processed) */

typedef struct MethodImage {
    
    /* Back reference to Class image data. */
    CrwClassImage *     ci;

    /* Unique method number for this class. */
    unsigned            number;

    /* Method name and descr */
    const char *        name;
    const char *        descr;

    /* Map of input bytecode offsets to output bytecode offsets */
    ByteOffset *        map;

    /* Bytecode injections for each input bytecode offset */
    Injection *         injections;

    /* Widening setting for each input bytecode offset */
    signed char *       widening;

    /* Length of original input bytecodes, and new bytecodes. */
    ByteOffset          code_len;
    ByteOffset          new_code_len;

    /* Location in input where bytecodes are located. */
    CrwPosition         start_of_input_bytecodes;

    /* Original max_stack and new max stack */
    unsigned            max_stack;
    unsigned            new_max_stack;
    
    jboolean            object_init_method;
    jboolean            skip_call_return_sites;

    /* Method access flags gotten from file. */
    unsigned            access_flags;
   
} MethodImage;

/* ----------------------------------------------------------------- */
/* General support functions (memory and error handling) */

static void 
fatal_error(CrwClassImage *ci, const char *message, const char *file, int line)
{
    if ( ci != NULL && ci->fatal_error_handler != NULL ) {
        (*ci->fatal_error_handler)(message, file, line);
    } else {
        /* Normal operation should NEVER reach here */
        /* NO CRW FATAL ERROR HANDLER! */
        (void)fprintf(stderr, "CRW: %s [%s:%d]\n", message, file, line);
        abort();
    }
}

#if defined(DEBUG) || !defined(NDEBUG)
static void
assert_error(CrwClassImage *ci, const char *condition, 
                 const char *file, int line)
{
    char buf[512];
    MethodImage *mi;
    ByteOffset byte_code_offset;

    mi = ci->current_mi;
    if ( mi != NULL ) {
        byte_code_offset = (ByteOffset)(mi->ci->input_position - mi->start_of_input_bytecodes);
    } else {
        byte_code_offset=-1;
    }
    
    (void)sprintf(buf,
                "CRW ASSERTION FAILURE: %s (%s:%s:%d)",
                condition, 
                ci->name==0?"?":ci->name,
                mi->name==0?"?":mi->name,
                byte_code_offset);
    fatal_error(ci, buf, file, line);
}
#endif

static void *
allocate(CrwClassImage *ci, int nbytes)
{
    void * ptr;
   
    if ( nbytes <= 0 ) {
        CRW_FATAL(ci, "Cannot allocate <= 0 bytes");
    }
    ptr = malloc(nbytes);
    if ( ptr == NULL ) {
        CRW_FATAL(ci, "Ran out of malloc memory"); 
    }
    return ptr;
}

static void *
reallocate(CrwClassImage *ci, void *optr, int nbytes)
{
    void * ptr;
   
    if ( optr == NULL ) {
        CRW_FATAL(ci, "Cannot deallocate NULL");
    }
    if ( nbytes <= 0 ) {
        CRW_FATAL(ci, "Cannot reallocate <= 0 bytes");
    }
    ptr = realloc(optr, nbytes);
    if ( ptr == NULL ) {
        CRW_FATAL(ci, "Ran out of malloc memory"); 
    }
    return ptr;
}

static void *
allocate_clean(CrwClassImage *ci, int nbytes)
{
    void * ptr;
   
    if ( nbytes <= 0 ) {
        CRW_FATAL(ci, "Cannot allocate <= 0 bytes");
    }
    ptr = calloc(nbytes, 1);
    if ( ptr == NULL ) {
        CRW_FATAL(ci, "Ran out of malloc memory"); 
    }
    return ptr;
}

static const char *
duplicate(CrwClassImage *ci, const char *str, int len)
{
    char *copy;
    
    copy = (char*)allocate(ci, len+1);
    (void)memcpy(copy, str, len);
    copy[len] = 0;
    return (const char *)copy;
}

static void deallocate(CrwClassImage *ci, void *ptr)
{
    if ( ptr == NULL ) {
        CRW_FATAL(ci, "Cannot deallocate NULL");
    }
    (void)free(ptr);
}

/* ----------------------------------------------------------------- */
/* Functions for reading/writing bytes to/from the class images */

static unsigned 
readU1(CrwClassImage *ci) 
{
    CRW_ASSERT_CI(ci);
    return ((unsigned)(ci->input[ci->input_position++])) & 0xFF;
}

static unsigned 
readU2(CrwClassImage *ci) 
{
    unsigned res;
    
    res = readU1(ci);
    return (res << 8) + readU1(ci);
}

static signed short
readS2(CrwClassImage *ci) 
{
    unsigned res;
    
    res = readU1(ci);
    return ((res << 8) + readU1(ci)) & 0xFFFF;
}

static unsigned 
readU4(CrwClassImage *ci) 
{
    unsigned res;
    
    res = readU2(ci);
    return (res << 16) + readU2(ci);
}

static void 
writeU1(CrwClassImage *ci, unsigned val)  /* Only writes out lower 8 bits */
{
    CRW_ASSERT_CI(ci);
    if ( ci->output != NULL ) {
        ci->output[ci->output_position++] = val & 0xFF;
    }
}

static void 
writeU2(CrwClassImage *ci, unsigned val) 
{
    writeU1(ci, val >> 8);
    writeU1(ci, val);
}

static void 
writeU4(CrwClassImage *ci, unsigned val) 
{
    writeU2(ci, val >> 16);
    writeU2(ci, val);
}

static unsigned 
copyU1(CrwClassImage *ci) 
{
    unsigned value;
    
    value = readU1(ci);
    writeU1(ci, value);
    return value;
}

static unsigned 
copyU2(CrwClassImage *ci) 
{
    unsigned value;
    
    value = readU2(ci);
    writeU2(ci, value);
    return value;
}

static unsigned 
copyU4(CrwClassImage *ci) 
{
    unsigned value;
    
    value = readU4(ci);
    writeU4(ci, value);
    return value;
}

static void 
copy(CrwClassImage *ci, unsigned count) 
{
    CRW_ASSERT_CI(ci);
    if ( ci->output != NULL ) {
        (void)memcpy(ci->output+ci->output_position, 
                     ci->input+ci->input_position, count);
        ci->output_position += count;
    }
    ci->input_position += count;
    CRW_ASSERT_CI(ci);
}

static void 
skip(CrwClassImage *ci, unsigned count) 
{
    CRW_ASSERT_CI(ci);
    ci->input_position += count;
}

static void
read_bytes(CrwClassImage *ci, void *bytes, unsigned count) 
{
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, bytes!=NULL);
    (void)memcpy(bytes, ci->input+ci->input_position, count);
    ci->input_position += count;
}

static void 
write_bytes(CrwClassImage *ci, void *bytes, unsigned count) 
{
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, bytes!=NULL);
    if ( ci->output != NULL ) {
        (void)memcpy(ci->output+ci->output_position, bytes, count);
        ci->output_position += count;
    }
}

static void 
random_writeU2(CrwClassImage *ci, CrwPosition pos, unsigned val) 
{
    CrwPosition save_position;
    
    CRW_ASSERT_CI(ci);
    save_position = ci->output_position;
    ci->output_position = pos;
    writeU2(ci, val);
    ci->output_position = save_position;
}

static void 
random_writeU4(CrwClassImage *ci, CrwPosition pos, unsigned val) 
{
    CrwPosition save_position;
    
    CRW_ASSERT_CI(ci);
    save_position = ci->output_position;
    ci->output_position = pos;
    writeU4(ci, val);
    ci->output_position = save_position;
}

/* ----------------------------------------------------------------- */
/* Constant Pool handling functions. */

static void
fillin_cpool_entry(CrwClassImage *ci, CrwCpoolIndex i,
                   ClassConstant tag,
                   unsigned int index1, unsigned int index2,
                   const char *ptr, int len)
{
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, i > 0 && i < ci->cpool_count_plus_one);
    ci->cpool[i].tag    = tag;
    ci->cpool[i].index1 = index1;
    ci->cpool[i].index2 = index2;
    ci->cpool[i].ptr    = ptr;
    ci->cpool[i].len    = (unsigned short)len;
}




static CrwCpoolIndex 
add_new_cpool_entry(CrwClassImage *ci, ClassConstant tag, 
                    unsigned int index1, unsigned int index2, 
                    const char *str, int len)
{
    CrwCpoolIndex i;
    char *utf8 = NULL;
    
    CRW_ASSERT_CI(ci);
    i = ci->cpool_count_plus_one++;

    /* NOTE: This implementation does not automatically expand the
     *       constant pool table beyond the expected number needed
     *       to handle this particular CrwTrackerInterface injections.
     *       See MAXIMUM_NEW_CPOOL_ENTRIES
     */
    CRW_ASSERT(ci,  ci->cpool_count_plus_one < ci->cpool_max_elements );
    
    writeU1(ci, tag);
    switch (tag) {
        case JVM_CONSTANT_Class: 
            writeU2(ci, index1);
            break;
        case JVM_CONSTANT_String:  
            writeU2(ci, index1);
            break;
        case JVM_CONSTANT_Fieldref: 
        case JVM_CONSTANT_Methodref: 
        case JVM_CONSTANT_InterfaceMethodref: 
        case JVM_CONSTANT_Integer:  
        case JVM_CONSTANT_Float:  
        case JVM_CONSTANT_NameAndType: 
            writeU2(ci, index1);
            writeU2(ci, index2);
            break;
        case JVM_CONSTANT_Long:  
        case JVM_CONSTANT_Double: 
            writeU4(ci, index1);
            writeU4(ci, index2);
            ci->cpool_count_plus_one++;
            CRW_ASSERT(ci,  ci->cpool_count_plus_one < ci->cpool_max_elements );
            break;
        case JVM_CONSTANT_Utf8:  
            CRW_ASSERT(ci, len==(len & 0xFFFF));
            writeU2(ci, len);
            write_bytes(ci, (void*)str, len);
            utf8 = (char*)duplicate(ci, str, len);
            break;
        default: 
            CRW_FATAL(ci, "Unknown constant"); 
            break;
    }
    fillin_cpool_entry(ci, i, tag, index1, index2, (const char *)utf8, len);
    CRW_ASSERT(ci, i > 0 && i < ci->cpool_count_plus_one);
    return i;
}

static CrwCpoolIndex
add_new_class_cpool_entry(CrwClassImage *ci, const char *class_name) 
{
    CrwCpoolIndex name_index;
    CrwCpoolIndex class_index;
    int           len;
    
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, class_name!=NULL);
        
    len = (int)strlen(class_name);
    name_index = add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0, 
                        class_name, len);
    class_index = add_new_cpool_entry(ci, JVM_CONSTANT_Class, name_index, 0, 
                        NULL, 0);
    return class_index;
}

static CrwCpoolIndex
add_new_method_cpool_entry(CrwClassImage *ci, CrwCpoolIndex class_index,
                     const char *name, const char *descr) 
{
    CrwCpoolIndex name_index;
    CrwCpoolIndex descr_index;
    CrwCpoolIndex name_type_index;
    int len;
    
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, name!=NULL);
    CRW_ASSERT(ci, descr!=NULL);
    len = (int)strlen(name);
    name_index = 
        add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0, name, len);
    len = (int)strlen(descr);
    descr_index = 
        add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0, descr, len);
    name_type_index = 
        add_new_cpool_entry(ci, JVM_CONSTANT_NameAndType, 
                                name_index, descr_index, NULL, 0);
    return add_new_cpool_entry(ci, JVM_CONSTANT_Methodref, 
                                class_index, name_type_index, NULL, 0);
}

static CrwConstantPoolEntry 
cpool_entry(CrwClassImage *ci, CrwCpoolIndex c_index) 
{
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, c_index > 0 && c_index < ci->cpool_count_plus_one);
    return ci->cpool[c_index];
}

static void 
cpool_setup(CrwClassImage *ci)
{
    CrwCpoolIndex i;
    CrwPosition cpool_output_position;
    int count_plus_one;
 
    CRW_ASSERT_CI(ci);
    cpool_output_position = ci->output_position;
    count_plus_one = copyU2(ci);
    CRW_ASSERT(ci, count_plus_one>1);
    ci->cpool_max_elements = count_plus_one+MAXIMUM_NEW_CPOOL_ENTRIES;
    ci->cpool = (CrwConstantPoolEntry*)allocate_clean(ci,
                (int)((ci->cpool_max_elements)*sizeof(CrwConstantPoolEntry)));
    ci->cpool_count_plus_one = (CrwCpoolIndex)count_plus_one;
  
    /* Index zero not in class file */
    for (i = 1; i < count_plus_one; ++i) {
        CrwCpoolIndex   ipos;
        ClassConstant   tag;
        unsigned int    index1;
        unsigned int    index2;
        unsigned        len;
        char *          utf8;
        
        ipos    = i;
        index1  = 0;
        index2  = 0;
        len     = 0;
        utf8    = NULL;
        
        tag = copyU1(ci);
        switch (tag) {
            case JVM_CONSTANT_Class: 
                index1 = copyU2(ci); 
                break;
            case JVM_CONSTANT_String:  
                index1 = copyU2(ci); 
                break;
            case JVM_CONSTANT_Fieldref: 
            case JVM_CONSTANT_Methodref: 
            case JVM_CONSTANT_InterfaceMethodref: 
            case JVM_CONSTANT_Integer:  
            case JVM_CONSTANT_Float:  
            case JVM_CONSTANT_NameAndType: 
                index1 = copyU2(ci); 
                index2 = copyU2(ci); 
                break;
            case JVM_CONSTANT_Long:  
            case JVM_CONSTANT_Double: 
                index1 = copyU4(ci); 
                index2 = copyU4(ci); 
                ++i;  /* // these take two CP entries - duh! */
                break;
            case JVM_CONSTANT_Utf8:  
                len     = copyU2(ci); 
                index1  = (unsigned short)len;
                utf8    = (char*)allocate(ci, len+1);
                read_bytes(ci, (void*)utf8, len);
                utf8[len] = 0;
                write_bytes(ci, (void*)utf8, len);
                break;
            default: 
                CRW_FATAL(ci, "Unknown constant"); 
                break;
        }
        fillin_cpool_entry(ci, ipos, tag, index1, index2, (const char *)utf8, len);
    }

    if (ci->call_name != NULL || ci->return_name != NULL) {
        if ( ci->number != (ci->number & 0x7FFF) ) {
            ci->class_number_index = 
                add_new_cpool_entry(ci, JVM_CONSTANT_Integer, 
                    (ci->number>>16) & 0xFFFF, ci->number & 0xFFFF, NULL, 0);
        }
    }

    if (  ci->tclass_name != NULL ) {
        ci->tracker_class_index = 
                add_new_class_cpool_entry(ci, ci->tclass_name);
    }
    if (ci->obj_init_name != NULL) {
        ci->object_init_tracker_index = add_new_method_cpool_entry(ci, 
                    ci->tracker_class_index, 
                    ci->obj_init_name, 
                    ci->obj_init_sig);
    }
    if (ci->newarray_name != NULL) {
        ci->newarray_tracker_index = add_new_method_cpool_entry(ci, 
                    ci->tracker_class_index, 
                    ci->newarray_name, 
                    ci->newarray_sig);
    }
    if (ci->call_name != NULL) {
        ci->call_tracker_index = add_new_method_cpool_entry(ci, 
                    ci->tracker_class_index, 
                    ci->call_name, 
                    ci->call_sig);
    }
    if (ci->return_name != NULL) {
        ci->return_tracker_index = add_new_method_cpool_entry(ci, 
                    ci->tracker_class_index, 
                    ci->return_name, 
                    ci->return_sig);
    }
    
    random_writeU2(ci, cpool_output_position, ci->cpool_count_plus_one);
}

/* ----------------------------------------------------------------- */
/* Functions that create the bytecodes to inject */

static ByteOffset
push_pool_constant_bytecodes(ByteCode *bytecodes, CrwCpoolIndex index)
{
    ByteOffset nbytes = 0;
    
    if ( index == (index&0x7F) ) {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_ldc;
    } else {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_ldc_w;
        bytecodes[nbytes++] = (ByteCode)((index >> 8) & 0xFF); 
    }
    bytecodes[nbytes++] = (ByteCode)(index & 0xFF);
    return nbytes;
}

static ByteOffset
push_short_constant_bytecodes(ByteCode *bytecodes, unsigned number)
{
    ByteOffset nbytes = 0;
    
    if ( number <= 5 ) {
        bytecodes[nbytes++] = (ByteCode)(JVM_OPC_iconst_0+number);
    } else if ( number == (number&0x7F) ) {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_bipush;
        bytecodes[nbytes++] = (ByteCode)(number & 0xFF);
    } else {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_sipush;
        bytecodes[nbytes++] = (ByteCode)((number >> 8) & 0xFF); 
        bytecodes[nbytes++] = (ByteCode)(number & 0xFF);
    }
    return nbytes;
}

static ByteOffset injection_template(MethodImage *mi, ByteCode *bytecodes, ByteOffset max_nbytes, 
                        CrwCpoolIndex method_index)
{
    CrwClassImage *     ci;
    ByteOffset nbytes = 0;
    unsigned max_stack;
    int add_dup;
    int add_aload;
    int push_cnum;
    int push_mnum;
    
    ci = mi->ci;
    
    CRW_ASSERT(ci, bytecodes!=NULL);
    
    if ( method_index == 0 )  {
        return 0;
    }

    if ( method_index == ci->newarray_tracker_index) {
        max_stack       = mi->max_stack + 1;
        add_dup         = JNI_TRUE;
        add_aload       = JNI_FALSE;
        push_cnum       = JNI_FALSE;
        push_mnum       = JNI_FALSE;
    } else if ( method_index == ci->object_init_tracker_index) {
        max_stack       = mi->max_stack + 1;
        add_dup         = JNI_FALSE;
        add_aload       = JNI_TRUE;
        push_cnum       = JNI_FALSE;
        push_mnum       = JNI_FALSE;
    } else {
        max_stack       = mi->max_stack + 2;
        add_dup         = JNI_FALSE;
        add_aload       = JNI_FALSE;
        push_cnum       = JNI_TRUE;
        push_mnum       = JNI_TRUE;
    }
    
    if ( add_dup ) {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_dup;
    }
    if ( add_aload ) {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_aload_0;
    }
    if ( push_cnum ) {
        if ( ci->number == (ci->number & 0x7FFF) ) {
            nbytes += push_short_constant_bytecodes(bytecodes+nbytes, 
                                                ci->number);
        } else {
            CRW_ASSERT(ci, ci->class_number_index!=0);
            nbytes += push_pool_constant_bytecodes(bytecodes+nbytes, 
                                                ci->class_number_index);
        }
    }
    if ( push_mnum ) {
        nbytes += push_short_constant_bytecodes(bytecodes+nbytes, 
                                            mi->number);
    }
    bytecodes[nbytes++] = (ByteCode)JVM_OPC_invokestatic;
    bytecodes[nbytes++] = (ByteCode)(method_index >> 8);
    bytecodes[nbytes++] = (ByteCode)method_index;
    bytecodes[nbytes]   = 0;

   /* bytecodes[nbytes++] = (ByteCode)JVM_OPC_getstatic;
	bytecodes[nbytes++] = (ByteCode)(ci->trace_field_index >> 8);
	bytecodes[nbytes++] = (ByteCode)ci->trace_field_index;
	bytecodes[nbytes++] = (ByteCode)opc_ifne;
	bytecodes[nbytes++] = (ByteCode)0;*/

	//bytecodes[nbytes++]=(ByteCode)opc_getstatic;
    CRW_ASSERT(ci, nbytes<max_nbytes);

    /* Make sure the new max_stack is appropriate */
    if ( max_stack > mi->new_max_stack ) {
        mi->new_max_stack = max_stack;
    }
    return nbytes;
}

/* Called to create injection code at entry to a method */
static ByteOffset
entry_injection_code(MethodImage *mi, ByteCode *bytecodes, ByteOffset len)
{
    CrwClassImage *     ci;
    ByteOffset nbytes = 0;
    
    CRW_ASSERT_MI(mi);

    ci = mi->ci;

    if ( mi->object_init_method ) {
        nbytes = injection_template(mi,
                            bytecodes, len, ci->object_init_tracker_index);
    }
    if ( !mi->skip_call_return_sites ) {
        nbytes += injection_template(mi,
                    bytecodes+nbytes, len-nbytes, ci->call_tracker_index);
    }
    return nbytes;
}

/* Called to create injection code before an opcode */
static ByteOffset
before_injection_code(MethodImage *mi, ClassOpcode opcode, 
                      ByteCode *bytecodes, ByteOffset len)
{
    ByteOffset nbytes = 0;

    
    CRW_ASSERT_MI(mi);
    switch ( opcode ) {
        case JVM_OPC_return:
        case JVM_OPC_ireturn:
        case JVM_OPC_lreturn:
        case JVM_OPC_freturn:
        case JVM_OPC_dreturn:
        case JVM_OPC_areturn:
            if ( !mi->skip_call_return_sites ) {
                nbytes = injection_template(mi,
                            bytecodes, len, mi->ci->return_tracker_index);
            }
            break;
        default:
            break;
    }
    return nbytes;
}

/* Called to create injection code after an opcode */
static ByteOffset
after_injection_code(MethodImage *mi, ClassOpcode opcode, 
                     ByteCode *bytecodes, ByteOffset len)
{
    CrwClassImage* ci;
    ByteOffset nbytes;
    
    ci = mi->ci;
    nbytes = 0;
   
    CRW_ASSERT_MI(mi);
    switch ( opcode ) {
        case JVM_OPC_new:
            /* Can't inject here cannot pass around uninitialized object */
            break;
        case JVM_OPC_newarray:
        case JVM_OPC_anewarray:
        case JVM_OPC_multianewarray:
            nbytes = injection_template(mi,
                                bytecodes, len, ci->newarray_tracker_index);
            break;
        default:
            break;
    }
    return nbytes;
}

/* Actually inject the bytecodes */
static void 
inject_bytecodes(MethodImage *mi, ByteOffset at, 
                 ByteCode *bytecodes, ByteOffset len) 
{
    Injection injection;
    CrwClassImage *ci;
    
    ci = mi->ci;
    CRW_ASSERT_MI(mi);
    CRW_ASSERT(ci, at <= mi->code_len);
    
    injection = mi->injections[at];
    
    CRW_ASSERT(ci, len <= LARGEST_INJECTION/2);
    CRW_ASSERT(ci, injection.len+len <= LARGEST_INJECTION);

    /* Either start an injection area or concatenate to what is there */
    if ( injection.code == NULL ) {
        CRW_ASSERT(ci, injection.len==0);
        injection.code = (ByteCode *)allocate_clean(ci, LARGEST_INJECTION+1);
    }
    
    (void)memcpy(injection.code+injection.len, bytecodes, len);
    injection.len += len;
    injection.code[injection.len] = 0;
    mi->injections[at] = injection;
    ci->injection_count++;
}

/* ----------------------------------------------------------------- */
/* Method handling functions */

static MethodImage *
method_init(CrwClassImage *ci, unsigned mnum, ByteOffset code_len)
{
    MethodImage *       mi;
    ByteOffset          i;
    
    mi                  = (MethodImage*)allocate_clean(ci, (int)sizeof(MethodImage));
    mi->ci              = ci;
    mi->name            = ci->method_name[mnum];
    mi->descr           = ci->method_descr[mnum];
    mi->code_len        = code_len;
    mi->map             = (ByteOffset*)allocate_clean(ci, 
                                (int)((code_len+1)*sizeof(ByteOffset)));
    for(i=0; i<=code_len; i++) {
        mi->map[i] = i;
    }
    mi->widening        = (signed char*)allocate_clean(ci, code_len+1);
    mi->injections      = (Injection *)allocate_clean(ci, 
                                (int)((code_len+1)*sizeof(Injection)));
    mi->number          = mnum;
    ci->current_mi      = mi;
    return mi;
}

static void
method_term(MethodImage *mi)
{
    CrwClassImage *ci;
    
    ci = mi->ci;
    CRW_ASSERT_MI(mi);
    if ( mi->map != NULL ) {
        deallocate(ci, (void*)mi->map);
        mi->map = NULL;
    }
    if ( mi->widening != NULL ) {
        deallocate(ci, (void*)mi->widening);
        mi->widening = NULL;
    }
    if ( mi->injections != NULL ) {
        ByteOffset i;
        for(i=0; i<= mi->code_len; i++) {
            if ( mi->injections[i].code != NULL ) {
                deallocate(ci, (void*)mi->injections[i].code);
                mi->injections[i].code = NULL;
            }
        }
        deallocate(ci, (void*)mi->injections);
        mi->injections = NULL;
    }
    ci->current_mi = NULL;
    deallocate(ci, (void*)mi);
}

static ByteOffset
input_code_offset(MethodImage *mi)
{
    CRW_ASSERT_MI(mi);
    return (ByteOffset)(mi->ci->input_position - mi->start_of_input_bytecodes);
}

static void
rewind_to_beginning_of_input_bytecodes(MethodImage *mi)
{
    CRW_ASSERT_MI(mi);
    mi->ci->input_position = mi->start_of_input_bytecodes;
}

/* Starting at original byte position 'at', add 'offset' to it's new
 *   location. This may be a negative value.
 *   NOTE: That this map is not the new bytecode location of the opcode
 *         but the new bytecode location that should be used when
 *         a goto or jump instruction was targeting the old bytecode
 *         location.
 */
static void 
adjust_map(MethodImage *mi, ByteOffset at, ByteOffset offset) 
{
    ByteOffset i;
    
    CRW_ASSERT_MI(mi);
    for (i = at; i <= mi->code_len; ++i) {
        mi->map[i] += offset;
    }
}

static void 
widen(MethodImage *mi, ByteOffset at, ByteOffset len) 
{
    int delta;
   
    CRW_ASSERT(mi->ci, at <= mi->code_len);
    delta = len - mi->widening[at];
    /* Adjust everything from the current input location by delta */
    adjust_map(mi, input_code_offset(mi), delta);
    /* Mark at beginning of instruction */
    mi->widening[at] = (signed char)len;
}

static void
verify_opc_wide(CrwClassImage *ci, ClassOpcode wopcode)
{
    switch (wopcode) {
        case JVM_OPC_aload: case JVM_OPC_astore:
        case JVM_OPC_fload: case JVM_OPC_fstore:
        case JVM_OPC_iload: case JVM_OPC_istore:
        case JVM_OPC_lload: case JVM_OPC_lstore:
        case JVM_OPC_dload: case JVM_OPC_dstore:
        case JVM_OPC_ret:   case JVM_OPC_iinc:
            break;
        default:
            CRW_FATAL(ci, "Invalid opcode supplied to wide opcode");
            break;
    }
}

static unsigned
opcode_length(CrwClassImage *ci, ClassOpcode opcode)
{
    /* Define array that holds length of an opcode */
    static unsigned char _opcode_length[JVM_OPC_MAX+1] = 
                          JVM_OPCODE_LENGTH_INITIALIZER;
    
    if ( opcode > JVM_OPC_MAX ) {
        CRW_FATAL(ci, "Invalid opcode supplied to opcode_length()");
    }
    return _opcode_length[opcode];
}
    
/* Walk one instruction and inject instrumentation */
static void 
inject_for_opcode(MethodImage *mi) 
{
    CrwClassImage *  ci;
    ClassOpcode      opcode;
    int              pos;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    pos = input_code_offset(mi);
    opcode = readU1(ci);
    
    if (opcode == JVM_OPC_wide) {
        ClassOpcode     wopcode;
        
        wopcode = readU1(ci);
        /* lvIndex not used */
        (void)readU2(ci);
        verify_opc_wide(ci, wopcode);
        if ( wopcode==JVM_OPC_iinc ) {
            (void)readU1(ci);
            (void)readU1(ci);
        }
    } else {
        
        ByteCode        bytecodes[LARGEST_INJECTION+1];
        int             header;
        int             instr_len;
        int             low;
        int             high;
        int             npairs;
        ByteOffset      len;

        /* Get bytecodes to inject before this opcode */
        len = before_injection_code(mi, opcode, bytecodes, (int)sizeof(bytecodes));
        if ( len > 0 ) {
            inject_bytecodes(mi, pos, bytecodes, len);
            /* Adjust map after processing this opcode */
        }

        /* Process this opcode */
        switch (opcode) {
            case JVM_OPC_tableswitch:
                header = NEXT_4BYTE_BOUNDARY(pos);
                skip(ci, header - (pos+1));
                (void)readU4(ci);
                low = readU4(ci);
                high = readU4(ci);
                skip(ci, (high+1-low) * 4);
                break;
            case JVM_OPC_lookupswitch:
                header = NEXT_4BYTE_BOUNDARY(pos);
                skip(ci, header - (pos+1));
                (void)readU4(ci);
                npairs = readU4(ci);
                skip(ci, npairs * 8);
                break;
            default:
                instr_len = opcode_length(ci, opcode);
                skip(ci, instr_len-1);
                break;
        }

        /* Get position after this opcode is processed */
        pos = input_code_offset(mi);

        /* Adjust for any before_injection_code() */
        if ( len > 0 ) {
            /* Adjust everything past this opcode.
             *   Why past it? Because we want any jumps to this bytecode loc
             *   to go to the injected code, not where the opcode
             *   was moved too.
             *   Consider a 'return' opcode that is jumped too.
             *   NOTE: This may not be correct in all cases, but will
             *         when we are only dealing with non-variable opcodes
             *         like the return opcodes. Be careful if the
             *         before_injection_code() changes to include other
             *         opcodes that have variable length.
             */
            adjust_map(mi, pos, len);
        }

        /* Get bytecodes to inject after this opcode */
        len = after_injection_code(mi, opcode, bytecodes, (int)sizeof(bytecodes));
        if ( len > 0 ) {
            inject_bytecodes(mi, pos, bytecodes, len);
        
            /* Adjust for any after_injection_code() */
            adjust_map(mi, pos, len);
        }
    
    }
}

/* Map original bytecode location to it's new location. (See adjust_map()). */
static ByteOffset
method_code_map(MethodImage *mi, ByteOffset pos)
{
    CRW_ASSERT_MI(mi);
    CRW_ASSERT(mi->ci, pos <= mi->code_len);
    return mi->map[pos];
}

static int 
adjust_instruction(MethodImage *mi) 
{
    CrwClassImage *     ci;
    ClassOpcode         opcode;
    int                 pos;
    int                 new_pos;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    pos = input_code_offset(mi);
    new_pos = method_code_map(mi,pos);
    
    opcode = readU1(ci);
    
    if (opcode == JVM_OPC_wide) {
        ClassOpcode wopcode;
        
        wopcode = readU1(ci);
        /* lvIndex not used */
        (void)readU2(ci);
        verify_opc_wide(ci, wopcode);
        if ( wopcode==JVM_OPC_iinc ) {
            (void)readU1(ci);
            (void)readU1(ci);
        }
    } else {
        
        int widened;
        int header;
        int newHeader;
        int low;
        int high;
        int new_pad;
        int old_pad;
        int delta;
        int new_delta;
        int delta_pad;
        int npairs;
        int instr_len;
        
        switch (opcode) {

        case JVM_OPC_tableswitch:
            widened     = mi->widening[pos];
            header      = NEXT_4BYTE_BOUNDARY(pos);
            newHeader   = NEXT_4BYTE_BOUNDARY(new_pos);

            skip(ci, header - (pos+1));

            delta       = readU4(ci);
            low         = readU4(ci);
            high        = readU4(ci);
            skip(ci, (high+1-low) * 4);
            new_pad     = newHeader - new_pos;
            old_pad     = header - pos;
            delta_pad   = new_pad - old_pad;
            if (widened != delta_pad) {
                widen(mi, pos, delta_pad);
                return 0;
            }
            break;

        case JVM_OPC_lookupswitch:
            widened     = mi->widening[pos];
            header      = NEXT_4BYTE_BOUNDARY(pos);
            newHeader   = NEXT_4BYTE_BOUNDARY(new_pos);

            skip(ci, header - (pos+1));

            delta       = readU4(ci);
            npairs      = readU4(ci);
            skip(ci, npairs * 8);
            new_pad     = newHeader - new_pos;
            old_pad     = header - pos;
            delta_pad   = new_pad - old_pad;
            if (widened != delta_pad) {
                widen(mi, pos, delta_pad);
                return 0;
            }
            break;

        case JVM_OPC_jsr: case JVM_OPC_goto:
        case JVM_OPC_ifeq: case JVM_OPC_ifge: case JVM_OPC_ifgt:
        case JVM_OPC_ifle: case JVM_OPC_iflt: case JVM_OPC_ifne:
        case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmpge:
        case JVM_OPC_if_icmpgt: case JVM_OPC_if_icmple: case JVM_OPC_if_icmplt:
        case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne:
        case JVM_OPC_ifnull: case JVM_OPC_ifnonnull:
            widened     = mi->widening[pos];
            delta       = readS2(ci);
            if (widened == 0) {
                new_delta = method_code_map(mi,pos+delta) - new_pos;
                if ((new_delta < -32768) || (new_delta > 32767)) {
                    switch (opcode) {
                        case JVM_OPC_jsr: case JVM_OPC_goto:
                            widen(mi, pos, 2);
                            break;
                        default:
                            widen(mi, pos, 5);
                            break;
                    }
                    return 0;
                }
            }
            break;

        case JVM_OPC_jsr_w:
        case JVM_OPC_goto_w:
            (void)readU4(ci);
            break;
                    
        default:
            instr_len = opcode_length(ci, opcode);
            skip(ci, instr_len-1);
            break;
        }
    }
    return 1;
}

static void 
write_instruction(MethodImage *mi) 
{
    CrwClassImage *     ci;
    ClassOpcode         opcode;
    ByteOffset          new_code_len;
    int                 pos;
    int                 new_pos;
    
    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    pos = input_code_offset(mi);
    new_pos = method_code_map(mi,pos);
    new_code_len = mi->injections[pos].len;
    if (new_code_len > 0) {
        write_bytes(ci, (void*)mi->injections[pos].code, new_code_len);
    }
    
    opcode = readU1(ci);
    if (opcode == JVM_OPC_wide) {
        ClassOpcode     wopcode;
        
        writeU1(ci, opcode);
        
        wopcode = copyU1(ci);
        /* lvIndex not used */
        (void)copyU2(ci);
        verify_opc_wide(ci, wopcode);
        if ( wopcode==JVM_OPC_iinc ) {
            (void)copyU1(ci);
            (void)copyU1(ci);
        }
    } else {
        
        ClassOpcode new_opcode;
        int             header;
        int             newHeader;
        int             low;
        int             high;
        int             i;
        int             npairs;
        int             widened;
        int             instr_len;
        int             delta;
        int             new_delta;

        switch (opcode) {

            case JVM_OPC_tableswitch:
                header = NEXT_4BYTE_BOUNDARY(pos);
                newHeader = NEXT_4BYTE_BOUNDARY(new_pos);

                skip(ci, header - (pos+1));

                delta = readU4(ci);
                new_delta = method_code_map(mi,pos+delta) - new_pos;
                low = readU4(ci);
                high = readU4(ci);

                writeU1(ci, opcode);
                for (i = new_pos+1; i < newHeader; ++i) {
                    writeU1(ci, 0);
                }
                writeU4(ci, new_delta);
                writeU4(ci, low);
                writeU4(ci, high);

                for (i = low; i <= high; ++i) {
                    delta = readU4(ci);
                    new_delta = method_code_map(mi,pos+delta) - new_pos;
                    writeU4(ci, new_delta);
                }
                break;

            case JVM_OPC_lookupswitch:
                header = NEXT_4BYTE_BOUNDARY(pos);
                newHeader = NEXT_4BYTE_BOUNDARY(new_pos);

                skip(ci, header - (pos+1));

                delta = readU4(ci);
                new_delta = method_code_map(mi,pos+delta) - new_pos;
                npairs = readU4(ci);
                writeU1(ci, opcode);
                for (i = new_pos+1; i < newHeader; ++i) {
                    writeU1(ci, 0);
                }
                writeU4(ci, new_delta);
                writeU4(ci, npairs);
                for (i = 0; i< npairs; ++i) {
                    unsigned match = readU4(ci);
                    delta = readU4(ci);
                    new_delta = method_code_map(mi,pos+delta) - new_pos;
                    writeU4(ci, match);
                    writeU4(ci, new_delta);
                }
                break;

            case JVM_OPC_jsr: case JVM_OPC_goto:
            case JVM_OPC_ifeq: case JVM_OPC_ifge: case JVM_OPC_ifgt:
            case JVM_OPC_ifle: case JVM_OPC_iflt: case JVM_OPC_ifne:
            case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmpge:
            case JVM_OPC_if_icmpgt: case JVM_OPC_if_icmple: case JVM_OPC_if_icmplt:
            case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne:
            case JVM_OPC_ifnull: case JVM_OPC_ifnonnull:
                widened = mi->widening[pos];
                delta = readS2(ci);
                new_delta = method_code_map(mi,pos+delta) - new_pos;
                new_opcode = opcode;
                if (widened == 0) {
                    writeU1(ci, opcode);
                    writeU2(ci, new_delta);
                } else if (widened == 2) {
                    switch (opcode) {
                        case JVM_OPC_jsr:
                            new_opcode = JVM_OPC_jsr_w;
                            break;
                        case JVM_OPC_goto:
                            new_opcode = JVM_OPC_goto_w;
                            break;
                        default:
                            CRW_FATAL(ci, "unexpected opcode");
                            break;
                    }
                    writeU1(ci, new_opcode);
                    writeU4(ci, new_delta);
                } else if (widened == 5) {
                    switch (opcode) {
                        case JVM_OPC_ifeq: 
                            new_opcode = JVM_OPC_ifne;
                            break;
                        case JVM_OPC_ifge: 
                            new_opcode = JVM_OPC_iflt;
                            break;
                        case JVM_OPC_ifgt:
                            new_opcode = JVM_OPC_ifle;
                            break;
                        case JVM_OPC_ifle: 
                            new_opcode = JVM_OPC_ifgt;
                            break;
                        case JVM_OPC_iflt: 
                            new_opcode = JVM_OPC_ifge;
                            break;
                        case JVM_OPC_ifne:
                            new_opcode = JVM_OPC_ifeq;
                            break;
                        case JVM_OPC_if_icmpeq: 
                            new_opcode = JVM_OPC_if_icmpne;
                            break;
                        case JVM_OPC_if_icmpne: 
                            new_opcode = JVM_OPC_if_icmpeq;
                            break;
                        case JVM_OPC_if_icmpge:
                            new_opcode = JVM_OPC_if_icmplt;
                            break;
                        case JVM_OPC_if_icmpgt: 
                            new_opcode = JVM_OPC_if_icmple;
                            break;
                        case JVM_OPC_if_icmple: 
                            new_opcode = JVM_OPC_if_icmpgt;
                            break;
                        case JVM_OPC_if_icmplt:
                            new_opcode = JVM_OPC_if_icmpge;
                            break;
                        case JVM_OPC_if_acmpeq: 
                            new_opcode = JVM_OPC_if_acmpne;
                            break;
                        case JVM_OPC_if_acmpne:
                            new_opcode = JVM_OPC_if_acmpeq;
                            break;
                        case JVM_OPC_ifnull: 
                            new_opcode = JVM_OPC_ifnonnull;
                            break;
                        case JVM_OPC_ifnonnull:
                            new_opcode = JVM_OPC_ifnull;
                            break;
                        default:
                            CRW_FATAL(ci, "Unexpected opcode");
                        break;
                    }
                    writeU1(ci, new_opcode);    /* write inverse branch */
                    writeU2(ci, 3 + 5);         /* beyond if and goto_w */
                    writeU1(ci, JVM_OPC_goto_w);    /* add a goto_w */
                    writeU4(ci, new_delta-3); /* write new and wide delta */
                } else {
                    CRW_FATAL(ci, "Unexpected widening");
                }
                break;

            case JVM_OPC_jsr_w:
            case JVM_OPC_goto_w:
                delta = readU4(ci);
                new_delta = method_code_map(mi,pos+delta) - new_pos;
                writeU1(ci, opcode);
                writeU4(ci, new_delta);
                break;
                        
            default:
                instr_len = opcode_length(ci, opcode);
                writeU1(ci, opcode); 
                copy(ci, instr_len-1);
                break;
        }
    }
}

static void 
method_inject_and_write_code(MethodImage *mi) 
{
    ByteCode bytecodes[LARGEST_INJECTION+1];
    ByteOffset   len;
    
    CRW_ASSERT_MI(mi);
   
    /* Do injections */
    rewind_to_beginning_of_input_bytecodes(mi);   
    len = entry_injection_code(mi, bytecodes, (int)sizeof(bytecodes));
    if ( len > 0 ) {
        int pos;

        pos = 0;
        inject_bytecodes(mi, pos, bytecodes, len);
        /* Adjust pos 0 to map to new pos 0, you never want to
         *  jump into this entry code injection. So the new pos 0
         *  will be past this entry_injection_code().
         */
        adjust_map(mi, pos, len); /* Inject before behavior */
    }
    while (input_code_offset(mi) < mi->code_len) {
        inject_for_opcode(mi);
    }
   
    /* Adjust instructions */
    rewind_to_beginning_of_input_bytecodes(mi);
    while (input_code_offset(mi) < mi->code_len) {
        if (!adjust_instruction(mi)) {
            rewind_to_beginning_of_input_bytecodes(mi);
        }
    }

    /* Write new instructions */
    rewind_to_beginning_of_input_bytecodes(mi); 
    while (input_code_offset(mi) < mi->code_len) {
        write_instruction(mi);
    }
}

static void 
copy_attribute(CrwClassImage *ci) 
{
    int len;
    
    (void)copyU2(ci);
    len = copyU4(ci);
    copy(ci, len);
}

static void 
copy_attributes(CrwClassImage *ci) 
{
    unsigned i;
    unsigned count;
   
    count = copyU2(ci);
    for (i = 0; i < count; ++i) {
        copy_attribute(ci);
    }
}

static void 
copy_all_fields(CrwClassImage *ci)
{
    unsigned i;
    unsigned count;
    
    count = copyU2(ci);
    for (i = 0; i < count; ++i) {
        /* access, name, descriptor */
        copy(ci, 6);
        copy_attributes(ci);
    }
}

static void
write_line_table(MethodImage *mi)
{
    unsigned             i;
    unsigned             count;
    CrwClassImage *      ci;
    
    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    (void)copyU4(ci);
    count = copyU2(ci);
    for(i=0; i<count; i++) {
        ByteOffset start_pc;
        ByteOffset new_start_pc;
        
        start_pc = readU2(ci);
        
        if ( start_pc == 0 ) {
            new_start_pc = 0; /* Don't skip entry injection code. */
        } else {
            new_start_pc = method_code_map(mi, start_pc);
        }
        
        writeU2(ci, new_start_pc);
        (void)copyU2(ci);
    }
}

/* Used for LocalVariableTable and LocalVariableTypeTable attributes */
static void
write_var_table(MethodImage *mi)
{
    unsigned             i;
    unsigned             count;
    CrwClassImage *      ci;
    
    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    (void)copyU4(ci);
    count = copyU2(ci);
    for(i=0; i<count; i++) {
        ByteOffset start_pc;
        ByteOffset new_start_pc;
        ByteOffset length;
        ByteOffset new_length;
        ByteOffset end_pc;
        ByteOffset new_end_pc;
        
        start_pc        = readU2(ci);
        length          = readU2(ci);

        if ( start_pc == 0 ) {
            new_start_pc = 0; /* Don't skip entry injection code. */
        } else {
            new_start_pc = method_code_map(mi, start_pc);
        }
        end_pc          = start_pc + length;
        new_end_pc      = method_code_map(mi, end_pc);
        new_length      = new_end_pc - new_start_pc;
        
        writeU2(ci, new_start_pc);
        writeU2(ci, new_length);
        (void)copyU2(ci);
        (void)copyU2(ci);
        (void)copyU2(ci);
    }
}

/* The uoffset field is u2 or u4 depending on the code_len.
 *   Note that the code_len is likely changing, so be careful here.
 */
static unsigned
readUoffset(MethodImage *mi)
{
    if ( mi->code_len > 65535 ) {
        return readU4(mi->ci);
    }
    return readU2(mi->ci);
}

static void
writeUoffset(MethodImage *mi, unsigned val)
{
    if ( mi->new_code_len > 65535 ) {
        writeU4(mi->ci, val);
    }
    writeU2(mi->ci, val);
}

static unsigned
copyUoffset(MethodImage *mi)
{
    unsigned uoffset;
    
    uoffset = readUoffset(mi);
    writeUoffset(mi, uoffset);
    return uoffset;
}

/* Copy over verification_type_info structure */
static void
copy_verification_types(MethodImage *mi, int ntypes)
{
    /* If there were ntypes, we just copy that over, no changes */
    if ( ntypes > 0 ) {
        int j;
                    
        for ( j = 0 ; j < ntypes ; j++ ) {
            unsigned tag;
            
            tag = copyU1(mi->ci);
            switch ( tag ) {
                case JVM_ITEM_Object:
                    (void)copyU2(mi->ci); /* Constant pool entry */
                    break;
                case JVM_ITEM_Uninitialized:
                    /* Code offset for 'new' opcode is for this object */
                    writeUoffset(mi, method_code_map(mi, readUoffset(mi)));
                    break;
            }
        }
    }
}

/* Process the StackMapTable attribute. We didn't add any basic blocks
 *   so the frame count remains the same but we may need to process the
 *   frame types due to offset changes putting things out of range.
 */
static void
write_stackmap_table(MethodImage *mi)
{
    CrwClassImage *ci;
    CrwPosition    save_position;
    ByteOffset     last_pc;
    ByteOffset     last_new_pc;
    unsigned       i;
    unsigned       attr_len;
    unsigned       new_attr_len;
    unsigned       count;
    unsigned       delta_adj;
    
    CRW_ASSERT_MI(mi);
    ci = mi->ci;

    /* Save the position of the attribute length so we can fix it later */
    save_position = ci->output_position;
    attr_len      = copyU4(ci);
    count         = copyUoffset(mi);  /* uoffset: number_of_entries */
    if ( count == 0 ) {
        CRW_ASSERT(ci, attr_len==2);
        return;
    }

    /* Process entire stackmap */
    last_pc     = 0;
    last_new_pc = 0;
    delta_adj   = 0;
    for ( i = 0 ; i < count ; i++ ) {
        ByteOffset new_pc;    /* new pc in instrumented code */
        unsigned   ft;        /* frame_type */
        int        delta;     /* pc delta */
        int        new_delta; /* new pc delta */

        ft = readU1(ci);
        if ( ft <= 63 ) {
            /* Frame Type: same_frame ([0,63]) */
            unsigned   new_ft;    /* new frame_type */
            
            delta     = (delta_adj + ft);
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            new_ft    = (new_delta - delta_adj);
            if ( new_ft > 63 ) {
                /* Change to same_frame_extended (251) */
                new_ft = 251;
                writeU1(ci, new_ft);
                writeUoffset(mi, (new_delta - delta_adj));
            } else {
                writeU1(ci, new_ft);
            }
        } else if ( ft >= 64 && ft <= 127 ) {
            /* Frame Type: same_locals_1_stack_item_frame ([64,127]) */
            unsigned   new_ft;    /* new frame_type */
            
            delta     = (delta_adj + ft - 64);
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            if ( (new_delta - delta_adj) > 63 ) {
                /* Change to same_locals_1_stack_item_frame_extended (247) */
                new_ft = 247;
                writeU1(ci, new_ft);
                writeUoffset(mi, (new_delta - delta_adj));
            } else {
                new_ft = (new_delta - delta_adj) + 64;
                writeU1(ci, new_ft);
            }
            copy_verification_types(mi, 1);
        } else if ( ft >= 128 && ft <= 246 ) {
            /* Frame Type: reserved_for_future_use ([128,246]) */
            CRW_FATAL(ci, "Unknown frame type in StackMapTable attribute");
        } else if ( ft == 247 ) {
            /* Frame Type: same_locals_1_stack_item_frame_extended (247) */
            delta     = (delta_adj + readUoffset(mi));
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            writeU1(ci, ft);
            writeUoffset(mi, (new_delta - delta_adj));
            copy_verification_types(mi, 1);
        } else if ( ft >= 248 && ft <= 250 ) {
            /* Frame Type: chop_frame ([248,250]) */
            delta     = (delta_adj + readUoffset(mi));
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            writeU1(ci, ft);
            writeUoffset(mi, (new_delta - delta_adj));
        } else if ( ft == 251 ) {
            /* Frame Type: same_frame_extended (251) */
            delta     = (delta_adj + readUoffset(mi));
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            writeU1(ci, ft);
            writeUoffset(mi, (new_delta - delta_adj));
        } else if ( ft >= 252 && ft <= 254 ) {
            /* Frame Type: append_frame ([252,254]) */
            delta     = (delta_adj + readUoffset(mi));
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            writeU1(ci, ft);
            writeUoffset(mi, (new_delta - delta_adj));
            copy_verification_types(mi, (ft - 251));
        } else if ( ft == 255 ) {
            unsigned   ntypes;

            /* Frame Type: full_frame (255) */
            delta     = (delta_adj + readUoffset(mi));
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            writeU1(ci, ft);
            writeUoffset(mi, (new_delta - delta_adj));
            ntypes    = copyU2(ci); /* ulocalvar */
            copy_verification_types(mi, ntypes);
            ntypes    = copyU2(ci); /* ustack */
            copy_verification_types(mi, ntypes);
        }

        /* Update last_pc and last_new_pc (save on calls to method_code_map) */
        CRW_ASSERT(ci, delta >= 0);
        CRW_ASSERT(ci, new_delta >= 0);
        last_pc    += delta;
        last_new_pc = new_pc;
        CRW_ASSERT(ci, last_pc <= mi->code_len);
        CRW_ASSERT(ci, last_new_pc <= mi->new_code_len);

        /* Delta adjustment, all deltas are -1 now in attribute */
        delta_adj = 1;
    }

    /* Update the attribute length */
    new_attr_len = ci->output_position - (save_position + 4);
    CRW_ASSERT(ci, new_attr_len >= attr_len);
    random_writeU4(ci, save_position, new_attr_len);
}

/* Process the CLDC StackMap attribute. We didn't add any basic blocks
 *   so the frame count remains the same but we may need to process the
 *   frame types due to offset changes putting things out of range.
 */
static void
write_cldc_stackmap_table(MethodImage *mi)
{
    CrwClassImage *ci;
    CrwPosition    save_position;
    unsigned       i;
    unsigned       attr_len;
    unsigned       new_attr_len;
    unsigned       count;
    
    CRW_ASSERT_MI(mi);
    ci = mi->ci;

    /* Save the position of the attribute length so we can fix it later */
    save_position = ci->output_position;
    attr_len      = copyU4(ci);
    count         = copyUoffset(mi);  /* uoffset: number_of_entries */
    if ( count == 0 ) {
        CRW_ASSERT(ci, attr_len==2);
        return;
    }

    /* Process entire stackmap */
    for ( i = 0 ; i < count ; i++ ) {
        unsigned   ntypes;

        writeUoffset(mi, method_code_map(mi, readUoffset(mi)));
        ntypes    = copyU2(ci); /* ulocalvar */
        copy_verification_types(mi, ntypes);
        ntypes    = copyU2(ci); /* ustack */
        copy_verification_types(mi, ntypes);
    }

    /* Update the attribute length */
    new_attr_len = ci->output_position - (save_position + 4);
    CRW_ASSERT(ci, new_attr_len >= attr_len);
    random_writeU4(ci, save_position, new_attr_len);
}

static void
method_write_exception_table(MethodImage *mi)
{
    unsigned            i;
    unsigned            count;
    CrwClassImage *     ci;
    
    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    count = copyU2(ci);
    for(i=0; i<count; i++) {
        ByteOffset start_pc;
        ByteOffset new_start_pc;
        ByteOffset end_pc;
        ByteOffset new_end_pc;
        ByteOffset handler_pc;
        ByteOffset new_handler_pc;
        
        start_pc        = readU2(ci);
        end_pc          = readU2(ci);
        handler_pc      = readU2(ci);
        
        new_start_pc    = method_code_map(mi, start_pc);
        new_end_pc      = method_code_map(mi, end_pc);
        new_handler_pc  = method_code_map(mi, handler_pc);
        
        writeU2(ci, new_start_pc);
        writeU2(ci, new_end_pc);
        writeU2(ci, new_handler_pc);
        (void)copyU2(ci);
    }
}

static int
attribute_match(CrwClassImage *ci, CrwCpoolIndex name_index, const char *name)
{
    CrwConstantPoolEntry cs;
    int                  len;
    
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, name!=NULL);
    len = (int)strlen(name);
    cs = cpool_entry(ci, name_index);
    if ( cs.len==len && strncmp(cs.ptr, name, len)==0) {
       return 1;
    }
    return 0;
}

static void 
method_write_code_attribute(MethodImage *mi)
{
    CrwClassImage *     ci;
    CrwCpoolIndex       name_index;
    
    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    name_index = copyU2(ci);
    if ( attribute_match(ci, name_index, "LineNumberTable") ) {
        write_line_table(mi);
    } else if ( attribute_match(ci, name_index, "LocalVariableTable") ) {
        write_var_table(mi);
    } else if ( attribute_match(ci, name_index, "LocalVariableTypeTable") ) {
        write_var_table(mi); /* Exact same format as the LocalVariableTable */
    } else if ( attribute_match(ci, name_index, "StackMapTable") ) {
        write_stackmap_table(mi);
    } else if ( attribute_match(ci, name_index, "StackMap") ) {
        write_cldc_stackmap_table(mi);
    } else {
        unsigned len;
        len = copyU4(ci);
        copy(ci, len);
    }
}

static int
is_init_method(const char *name)
{
    if ( name!=NULL && strcmp(name,"<init>")==0 ) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}

static int
is_clinit_method(const char *name)
{
    if ( name!=NULL && strcmp(name,"<clinit>")==0 ) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}

static int
is_finalize_method(const char *name)
{
    if ( name!=NULL && strcmp(name,"finalize")==0 ) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}

static int
skip_method(CrwClassImage *ci, const char *name, 
                unsigned access_flags, ByteOffset code_len, 
                int system_class, jboolean *pskip_call_return_sites)
{
    *pskip_call_return_sites = JNI_FALSE;
    if ( system_class ) {
        if ( code_len == 1 && is_init_method(name) ) {
            return JNI_TRUE;
        } else if ( code_len == 1 && is_finalize_method(name) ) {
            return JNI_TRUE;
        } else if ( is_clinit_method(name) ) {
            return JNI_TRUE;
        } else if ( ci->is_thread_class && strcmp(name,"currentThread")==0 ) {
            return JNI_TRUE;
        }
        /*
        if ( access_flags & JVM_ACC_PRIVATE ) {
            *pskip_call_return_sites = JNI_TRUE;
        }
        */
    }
    return JNI_FALSE;
}

/* Process all code attributes */
static void 
method_write_bytecodes(CrwClassImage *ci, unsigned mnum, unsigned access_flags)
{
    CrwPosition         output_attr_len_position;
    CrwPosition         output_max_stack_position;
    CrwPosition         output_code_len_position;
    CrwPosition         start_of_output_bytecodes;
    unsigned            i;
    unsigned            attr_len;
    unsigned            max_stack;
    ByteOffset          code_len;
    unsigned            attr_count;
    unsigned            new_attr_len;
    MethodImage *       mi;
    jboolean            object_init_method;
    jboolean            skip_call_return_sites;

    CRW_ASSERT_CI(ci);
    
    /* Attribute Length */
    output_attr_len_position = ci->output_position;
    attr_len = copyU4(ci);
    
    /* Max Stack */
    output_max_stack_position = ci->output_position;
    max_stack = copyU2(ci);
    
    /* Max Locals */
    (void)copyU2(ci);
    
    /* Code Length */
    output_code_len_position = ci->output_position;
    code_len = copyU4(ci);
    start_of_output_bytecodes = ci->output_position;
   
    /* Some methods should not be instrumented */
    object_init_method = JNI_FALSE;
    skip_call_return_sites = JNI_FALSE;
    if ( ci->is_object_class &&
         is_init_method(ci->method_name[mnum]) &&
         strcmp(ci->method_descr[mnum],"()V")==0 ) {
        object_init_method = JNI_TRUE;
        skip_call_return_sites = JNI_TRUE;
    } else if ( skip_method(ci, ci->method_name[mnum], access_flags, 
                code_len, ci->system_class, &skip_call_return_sites) ) {
        /* Copy remainder minus already copied, the U2 max_stack, 
         *   U2 max_locals, and U4 code_length fields have already 
         *   been processed.
         */
        copy(ci, attr_len - (2+2+4));
        return;
    }
   
    /* Start Injection */
    mi = method_init(ci, mnum, code_len);
    mi->object_init_method = object_init_method;
    mi->access_flags = access_flags;
    mi->skip_call_return_sites = skip_call_return_sites;

    /* Save the current position as the start of the input bytecodes */
    mi->start_of_input_bytecodes = ci->input_position;

    /* The max stack may increase */
    mi->max_stack = max_stack;
    mi->new_max_stack = max_stack;

    /* Adjust all code offsets */
    method_inject_and_write_code(mi);

    /* Fix up code length (save new_code_len for later attribute processing) */
    mi->new_code_len = (int)(ci->output_position - start_of_output_bytecodes);
    random_writeU4(ci, output_code_len_position, mi->new_code_len);

    /* Fixup max stack */
    CRW_ASSERT(ci, mi->new_max_stack <= 0xFFFF);
    random_writeU2(ci, output_max_stack_position, mi->new_max_stack);
    
    /* Copy exception table */
    method_write_exception_table(mi);

    /* Copy code attributes (needs mi->new_code_len) */
    attr_count = copyU2(ci);
    for (i = 0; i < attr_count; ++i) {
        method_write_code_attribute(mi);
    }

    /* Fix up attribute length */
    new_attr_len = (int)(ci->output_position - (output_attr_len_position + 4));
    random_writeU4(ci, output_attr_len_position, new_attr_len);

    /* Free method data */
    method_term(mi);
    mi = NULL;

}        

static void 
method_write(CrwClassImage *ci, unsigned mnum) 
{
    unsigned            i;
    unsigned            access_flags;
    CrwCpoolIndex       name_index;
    CrwCpoolIndex       descr_index;
    unsigned            attr_count;
    
    access_flags = copyU2(ci);
    name_index = copyU2(ci);
    ci->method_name[mnum] = cpool_entry(ci, name_index).ptr;
    descr_index = copyU2(ci);
    ci->method_descr[mnum] = cpool_entry(ci, descr_index).ptr;
    attr_count = copyU2(ci);
    
    for (i = 0; i < attr_count; ++i) {
        CrwCpoolIndex name_index;
        
        name_index = copyU2(ci);
        if ( attribute_match(ci, name_index, "Code") ) {
            method_write_bytecodes(ci, mnum, access_flags);
        } else {
            unsigned len;
            len = copyU4(ci);
            copy(ci, len);
        }
    }
}

static void 
method_write_all(CrwClassImage *ci) 
{
    unsigned i;
    unsigned count;
    
    count = copyU2(ci);
    ci->method_count = count;
    if ( count > 0 ) {
        ci->method_name = (const char **)allocate_clean(ci, count*(int)sizeof(const char*));
        ci->method_descr = (const char **)allocate_clean(ci, count*(int)sizeof(const char*));
    }
    
    for (i = 0; i < count; ++i) {
        method_write(ci, i);
    }
    
    if ( ci->mnum_callback != NULL ) {
        (*(ci->mnum_callback))(ci->number, ci->method_name, ci->method_descr, 
                         count);
    }
}

/* ------------------------------------------------------------------- */
/* Cleanup function. */

static void
cleanup(CrwClassImage *ci)
{
    CRW_ASSERT_CI(ci);
    if ( ci->name != NULL ) {
        deallocate(ci, (void*)ci->name);
        ci->name = NULL;
    }
    if ( ci->method_name != NULL ) {
        deallocate(ci, (void*)ci->method_name);
        ci->method_name = NULL;
    }
    if ( ci->method_descr != NULL ) {
        deallocate(ci, (void*)ci->method_descr);
        ci->method_descr = NULL;
    }
    if ( ci->cpool != NULL ) {
        CrwCpoolIndex i;
        for(i=0; i<ci->cpool_count_plus_one; i++) {
            if ( ci->cpool[i].ptr != NULL ) {
                deallocate(ci, (void*)(ci->cpool[i].ptr));
                ci->cpool[i].ptr = NULL;
            }
        }
        deallocate(ci, (void*)ci->cpool);
        ci->cpool = NULL;
    }
}

static jboolean
skip_class(unsigned access_flags)
{
    if ( access_flags & JVM_ACC_INTERFACE ) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}

static long 
inject_class(struct CrwClassImage *ci, 
                 int system_class,
                 char* tclass_name,     
                 char* tclass_sig,      
                 char* call_name,       
                 char* call_sig,        
                 char* return_name,     
                 char* return_sig,      
                 char* obj_init_name,   
                 char* obj_init_sig,    
                 char* newarray_name,   
                 char* newarray_sig,    
                 unsigned char *buf, 
                 long buf_len)
{
    CrwConstantPoolEntry        cs;
    CrwCpoolIndex               this_class;
    CrwCpoolIndex               super_class;
    unsigned                    magic;
    unsigned                    classfileVersion;
    unsigned                    interface_count;

    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, buf!=NULL);
    CRW_ASSERT(ci, buf_len!=0);
    
    CRW_ASSERT(ci, strchr(tclass_name,'.')==NULL); /* internal qualified name */
    
    ci->injection_count         = 0;
    ci->system_class            = system_class;
    ci->tclass_name             = tclass_name;
    ci->tclass_sig              = tclass_sig;
    ci->call_name               = call_name;
    ci->call_sig                = call_sig;
    ci->return_name             = return_name;
    ci->return_sig              = return_sig;
    ci->obj_init_name           = obj_init_name;
    ci->obj_init_sig            = obj_init_sig;
    ci->newarray_name           = newarray_name;
    ci->newarray_sig            = newarray_sig;
    ci->output                  = buf;
    ci->output_len              = buf_len;
    
    magic = copyU4(ci);
    CRW_ASSERT(ci, magic==0xCAFEBABE);
    if ( magic != 0xCAFEBABE ) {
        return (long)0;
    }

    /* minor version number not used */
    (void)copyU2(ci);
    /* major version number not used */
    classfileVersion = copyU2(ci);
    CRW_ASSERT(ci, classfileVersion <= 50); /* Mustang class files or less */
    
    cpool_setup(ci);

    ci->access_flags        = copyU2(ci);
    if ( skip_class(ci->access_flags) ) {
        return (long)0;
    }
    
    this_class          = copyU2(ci);
    
    cs = cpool_entry(ci, (CrwCpoolIndex)(cpool_entry(ci, this_class).index1));
    if ( ci->name == NULL ) {
        ci->name = duplicate(ci, cs.ptr, cs.len);
        CRW_ASSERT(ci, strchr(ci->name,'.')==NULL); /* internal qualified name */
    }
    CRW_ASSERT(ci, (int)strlen(ci->name)==cs.len && strncmp(ci->name, cs.ptr, cs.len)==0);
    
    super_class         = copyU2(ci);
    if ( super_class == 0 ) {
        ci->is_object_class = JNI_TRUE;
        CRW_ASSERT(ci, strcmp(ci->name,"java/lang/Object")==0);
    }

    interface_count     = copyU2(ci);
    copy(ci, interface_count * 2);

    copy_all_fields(ci);

    method_write_all(ci);
    
    if ( ci->injection_count == 0 ) {
        return (long)0;
    }
    
    copy_attributes(ci);

    return (long)ci->output_position;
}

/* ------------------------------------------------------------------- */
/* Exported interfaces */

JNIEXPORT void JNICALL 
java_crw_demo(unsigned class_number,
         const char *name,
         const unsigned char *file_image,
         long file_len, 
         int system_class,
         char* tclass_name,     /* Name of class that has tracker methods. */
         char* tclass_sig,      /* Signature of tclass */
         char* call_name,       /* Method name to call at offset 0 */
         char* call_sig,        /* Signature of this method */
         char* return_name,     /* Method name to call before any return */
         char* return_sig,      /* Signature of this method */
         char* obj_init_name,   /* Method name to call in Object <init> */
         char* obj_init_sig,    /* Signature of this method */
         char* newarray_name,   /* Method name to call after newarray opcodes */
         char* newarray_sig,    /* Signature of this method */
         unsigned char **pnew_file_image, 
         long *pnew_file_len,
         FatalErrorHandler fatal_error_handler,
         MethodNumberRegister mnum_callback)
{
    CrwClassImage ci;
    long          max_length;
    long          new_length;
    void         *new_image;
    int           len;

    /* Initial setup of the CrwClassImage structure */
    (void)memset(&ci, 0, (int)sizeof(CrwClassImage));
    ci.fatal_error_handler = fatal_error_handler;
    ci.mnum_callback       = mnum_callback;
    
    /* Do some interface error checks */
    if ( pnew_file_image==NULL ) {
        CRW_FATAL(&ci, "pnew_file_image==NULL");
    }
    if ( pnew_file_len==NULL ) {
        CRW_FATAL(&ci, "pnew_file_len==NULL");
    }
    
    /* No file length means do nothing */
    *pnew_file_image = NULL;
    *pnew_file_len = 0;
    if ( file_len==0 ) {
        return;
    }

    /* Do some more interface error checks */
    if ( file_image == NULL ) {
        CRW_FATAL(&ci, "file_image == NULL");
    }
    if ( file_len < 0 ) {
        CRW_FATAL(&ci, "file_len < 0");
    }
    if ( system_class != 0 && system_class != 1 ) {
        CRW_FATAL(&ci, "system_class is not 0 or 1");
    }
    if ( tclass_name == NULL ) {
        CRW_FATAL(&ci, "tclass_name == NULL");
    }
    if ( tclass_sig == NULL || tclass_sig[0]!='L' ) {
        CRW_FATAL(&ci, "tclass_sig is not a valid class signature");
    }
    len = (int)strlen(tclass_sig);
    if ( tclass_sig[len-1]!=';' ) {
        CRW_FATAL(&ci, "tclass_sig is not a valid class signature");
    }
    if ( call_name != NULL ) {
        if ( call_sig == NULL || strcmp(call_sig, "(II)V") != 0 ) {
            CRW_FATAL(&ci, "call_sig is not (II)V");
        }
    }
    if ( return_name != NULL ) {
        if ( return_sig == NULL || strcmp(return_sig, "(II)V") != 0 ) {
            CRW_FATAL(&ci, "return_sig is not (II)V");
        }
    }
    if ( obj_init_name != NULL ) {
        if ( obj_init_sig == NULL || strcmp(obj_init_sig, "(Ljava/lang/Object;)V") != 0 ) {
            CRW_FATAL(&ci, "obj_init_sig is not (Ljava/lang/Object;)V");
        }
    }
    if ( newarray_name != NULL ) {
        if ( newarray_sig == NULL || strcmp(newarray_sig, "(Ljava/lang/Object;)V") != 0 ) {
            CRW_FATAL(&ci, "newarray_sig is not (Ljava/lang/Object;)V");
        }
    }
    
    /* Finish setup the CrwClassImage structure */
    ci.is_thread_class = JNI_FALSE; 
    if ( name != NULL ) {
        CRW_ASSERT(&ci, strchr(name,'.')==NULL); /* internal qualified name */
    
        ci.name = duplicate(&ci, name, (int)strlen(name));
        if ( strcmp(name, "java/lang/Thread")==0 ) {
            ci.is_thread_class = JNI_TRUE; 
        }
    }
    ci.number = class_number;
    ci.input = file_image;
    ci.input_len = file_len;

    /* Do the injection */
    max_length = file_len*2 + 512; /* Twice as big + 512 */
    new_image = allocate(&ci, (int)max_length);
    new_length = inject_class(&ci, 
                                 system_class, 
                                 tclass_name,     
                                 tclass_sig,      
                                 call_name,       
                                 call_sig,        
                                 return_name,     
                                 return_sig,      
                                 obj_init_name,   
                                 obj_init_sig,    
                                 newarray_name,   
                                 newarray_sig,    
                                 (unsigned char*)new_image, 
                                 max_length);
   
    /* Dispose or shrink the space to be returned. */
    if ( new_length == 0 ) {
        deallocate(&ci, (void*)new_image);
        new_image = NULL;
    } else {
        new_image = (void*)reallocate(&ci, (void*)new_image, (int)new_length);
    }
   
    /* Return the new class image */
    *pnew_file_image = (unsigned char *)new_image;
    *pnew_file_len = (long)new_length;

    /* Cleanup before we leave. */
    cleanup(&ci);
}

/* Return the classname for this class which is inside the classfile image. */
JNIEXPORT char * JNICALL 
java_crw_demo_classname(const unsigned char *file_image, long file_len,
        FatalErrorHandler fatal_error_handler)
{
    CrwClassImage               ci;
    CrwConstantPoolEntry        cs;
    CrwCpoolIndex               this_class;
    unsigned                    magic;
    char *                      name;

    name = NULL;
    
    if ( file_len==0 || file_image==NULL ) {
        return name;
    }

    /* The only fields we need filled in are the image pointer and the error
     *    handler.
     *    By not adding an output buffer pointer, no output is created.
     */
    (void)memset(&ci, 0, (int)sizeof(CrwClassImage));
    ci.input     = file_image;
    ci.input_len = file_len;
    ci.fatal_error_handler = fatal_error_handler;

    /* Read out the bytes from the classfile image */

    magic = readU4(&ci); /* magic number */
    CRW_ASSERT(&ci, magic==0xCAFEBABE);
    if ( magic != 0xCAFEBABE ) {
        return name;
    }
    (void)readU2(&ci); /* minor version number */
    (void)readU2(&ci); /* major version number */
   
    /* Read in constant pool. Since no output setup, writes are NOP's */
    cpool_setup(&ci);

    (void)readU2(&ci); /* access flags */
    this_class = readU2(&ci); /* 'this' class */
   
    /* Get 'this' constant pool entry */
    cs = cpool_entry(&ci, (CrwCpoolIndex)(cpool_entry(&ci, this_class).index1));
    
    /* Duplicate the name */
    name = (char *)duplicate(&ci, cs.ptr, cs.len);
    
    /* Cleanup before we leave. */
    cleanup(&ci);

    /* Return malloc space */
    return name;
}

3、test.cpp

/*
 * @(#)minst.c	1.1 06/01/28
 * 
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 * 
 * -Redistribution in binary form must reproduce the above copyright notice, 
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may 
 * be used to endorse or promote products derived from this software without 
 * specific prior written permission.
 * 
 * This software is provided "AS IS," without a warranty of any kind. ALL 
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST 
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, 
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY 
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, 
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * 
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include "stdlib.h"
#include <jni_md.h>
#include <jvmti.h> 
#include "java_crw_demo.h"
#include <agent_util.h>
	 
	   
/* ------------------------------------------------------------------- */
/* Some constant maximum sizes */

#define MAX_TOKEN_LENGTH	80
#define MAX_METHOD_NAME_LENGTH  256

/* Some constant names that tie to Java class/method names.
 *    We assume the Java class whose static methods we will be calling
 *    looks like:
 *
 * public class Minst {
 *     private static int engaged; 
 *     private static native void _method_entry(Object thr, int cnum, int mnum);
 *     public static void method_entry(int cnum, int mnum)
 *     {
 *   	   ...
 *     }
 * }
 *
 */

#define MINST_class	   Minst	    /* Name of class we are using */
#define MINST_entry        method_entry    /* Name of java entry method */
#define MINST_engaged      engaged         /* Name of java static field */

/* C macros to create strings from tokens */
#define _STRING(s) #s
#define STRING(s) _STRING(s)

/* ------------------------------------------------------------------- */

/* Global agent data structure */

typedef struct {
    /* JVMTI Environment */
    jvmtiEnv      *jvmti;
    jboolean       vm_is_dead;
    jboolean       vm_is_started;
    /* Data access Lock */
    jrawMonitorID  lock;
    /* Options */
    char           *include;
    char           *exclude;
    /* Class Count/ID */
    jint            ccount;
} GlobalAgentData;

static GlobalAgentData *gdata;

/* Enter a critical section by doing a JVMTI Raw Monitor Enter */
static void
enter_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error;
    
    error = (jvmti)->RawMonitorEnter(gdata->lock);
    check_jvmti_error(jvmti, error, "Cannot enter with raw monitor");
}

/* Exit a critical section by doing a JVMTI Raw Monitor Exit */
static void
exit_critical_section(jvmtiEnv *jvmti)
{
    jvmtiError error;
    
    error = (jvmti)->RawMonitorExit(gdata->lock);
    check_jvmti_error(jvmti, error, "Cannot exit with raw monitor");
}

/* Callback for JVMTI_EVENT_VM_START */
static void JNICALL cbVMStart(jvmtiEnv *jvmti, JNIEnv *env)
{
    enter_critical_section(jvmti); {
        /* Indicate VM has started */
        gdata->vm_is_started = JNI_TRUE;
    } exit_critical_section(jvmti);
}

/* Callback for JVMTI_EVENT_VM_INIT */
static void JNICALL cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
{
    enter_critical_section(jvmti); {
        jclass   klass;
	jfieldID field;

	/* Register Natives for class whose methods we use */
	klass = (env)->FindClass(STRING(MINST_class));
	if ( klass == NULL ) {
	    fatal_error("ERROR: JNI: Cannot find %s with FindClass\n", 
			STRING(MINST_class));
	}
	
	/* Engage calls. */
	field = (env)->GetStaticFieldID(klass, STRING(MINST_engaged), "I");
	if ( field == NULL ) {
	    fatal_error("ERROR: JNI: Cannot get field from %s\n", 
			STRING(MINST_class));
	}
	(env)->SetStaticIntField(klass, field, 1);
    } exit_critical_section(jvmti);
}

/* Callback for JVMTI_EVENT_VM_DEATH */
static void JNICALL cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env)
{
    enter_critical_section(jvmti); {
        jclass   klass;
	jfieldID field;

	/* The VM has died. */
	stdout_message("VMDeath\n");

	/* Disengage calls in MINST_class. */
	klass = (env)->FindClass(STRING(MINST_class));
	if ( klass == NULL ) {
	    fatal_error("ERROR: JNI: Cannot find %s with FindClass\n", 
			STRING(MINST_class));
	}
	field = (env)->GetStaticFieldID(klass, STRING(MINST_engaged), "I");
	if ( field == NULL ) {
	    fatal_error("ERROR: JNI: Cannot get field from %s\n", 
			STRING(MINST_class));
	}
	(env)->SetStaticIntField(klass, field, -1);

	/* The critical section here is important to hold back the VM death
	 *    until all other callbacks have completed.
	 */

	/* Since this critical section could be holding up other threads
	 *   in other event callbacks, we need to indicate that the VM is
	 *   dead so that the other callbacks can short circuit their work.
	 *   We don't expect any further events after VmDeath but we do need
	 *   to be careful that existing threads might be in our own agent
	 *   callback code.
	 */
	gdata->vm_is_dead = JNI_TRUE;

    } exit_critical_section(jvmti);
	
}  

/* Callback for JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
static void JNICALL cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env,
                jclass class_being_redefined, jobject loader,
                const char* name, jobject protection_domain,
                jint class_data_len, const unsigned char* class_data,
                jint* new_class_data_len, unsigned char** new_class_data)
{
    enter_critical_section(jvmti); {
	/* It's possible we get here right after VmDeath event, be careful */
	if ( !gdata->vm_is_dead ) {

	    const char *classname;

            /* Name could be NULL */
	    if ( name == NULL ) {
		classname = java_crw_demo_classname(class_data, class_data_len,NULL);
		if ( classname == NULL ) {
		    fatal_error("ERROR: No classname inside classfile\n");
		}
	    } else {
		classname = strdup(name);
		if ( classname == NULL ) {
		    fatal_error("ERROR: Out of malloc memory\n");
		}
	    }
	    
	    *new_class_data_len = 0;
            *new_class_data     = NULL;
			
            /* The tracker class itself? */
            if ( interested((char*)classname, "", gdata->include, gdata->exclude) &&  strcmp(classname, STRING(MINST_class)) != 0 ) {
                jint           cnum;
                int            system_class;
                unsigned char *new_image;
                long           new_length;

                /* Get unique number for every class file image loaded */
                cnum = gdata->ccount++;
                /* Is it a system class? If the class load is before VmStart
				 *   then we will consider it a system class that should
				 *   be treated carefully. (See java_crw_demo)
				 */
                system_class = 0;
                if ( !gdata->vm_is_started ) {
                    system_class = 1;
                }

                new_image = NULL;
                new_length = 0;

                /* Call the class file reader/write demo code */
                java_crw_demo(cnum,
                    classname,
                    class_data,
                    class_data_len,
                    system_class,
                    STRING(MINST_class), "L" STRING(MINST_class) ";",
                    STRING(MINST_entry), "(II)V",
                    NULL, NULL,
                    NULL, NULL,
                    NULL, NULL,
                    &new_image,
                    &new_length,
                    NULL,
                    NULL);

				/* If we got back a new class image, return it back as "the"
				 *   new class image. This must be JVMTI Allocate space.
				 */
                if ( new_length > 0 ) {
                    unsigned char *jvmti_space;

                    jvmti_space = (unsigned char *)allocate(jvmti, (jint)new_length);
                    (void)memcpy((void*)jvmti_space, (void*)new_image, (int)new_length);
                    *new_class_data_len = (jint)new_length;
                    *new_class_data     = jvmti_space; /* VM will deallocate */
                }

				/* Always free up the space we get from java_crw_demo() */
                if ( new_image != NULL ) {
                    (void)free((void*)new_image); /* Free malloc() space with free() */
                }
            }
	    (void)free((void*)classname);
	}
    } exit_critical_section(jvmti);
}

/* Parse the options for this minst agent */
static void parse_agent_options(char *options)
{
    char token[MAX_TOKEN_LENGTH];
    char *next;

    /* Parse options and set flags in gdata */
    if ( options==NULL ) {
	return;
    }
   
    /* Get the first token from the options string. */
    next = get_token(options, ",=", token, sizeof(token));

    /* While not at the end of the options string, process this option. */
    while ( next != NULL ) {
	if ( strcmp(token,"help")==0 ) {
	    stdout_message("The minst JVMTI demo agent\n");
	    stdout_message("\n");
	    stdout_message(" java -agent:minst[=options] ...\n");
	    stdout_message("\n");
	    stdout_message("The options are comma separated:\n");
	    stdout_message("\t help\t\t\t Print help information\n");
	    stdout_message("\t include=item\t\t Only these classes/methods\n");
	    stdout_message("\t exclude=item\t\t Exclude these classes/methods\n");
	    stdout_message("\n");
	    stdout_message("item\t Qualified class and/or method names\n");
	    stdout_message("\t\t e.g. (*.<init>;Foobar.method;sun.*)\n");
	    stdout_message("\n");
	    exit(0);
	} else if ( strcmp(token,"include")==0 ) {
	    int   used;
	    int   maxlen;

	    maxlen = MAX_METHOD_NAME_LENGTH;
	    if ( gdata->include == NULL ) {
			gdata->include = (char*)calloc(maxlen+1, 1);
			used = 0;
	    } else {
			used  = (int)strlen(gdata->include);
			gdata->include[used++] = ',';
			gdata->include[used] = 0;
			gdata->include = (char*)realloc((void*)gdata->include, used+maxlen+1);
	    }
	    if ( gdata->include == NULL ) {
			fatal_error("ERROR: Out of malloc memory\n");
	    }
		
	    /* Add this item to the list */
	    next = get_token(next, ",=", gdata->include+used, maxlen);
		printf("下一个规则%s",next);
	    /* Check for token scan error */
	    if ( next==NULL ) {
			fatal_error("ERROR: include option error\n");
	    }
	} else if ( strcmp(token,"exclude")==0 ) {
	    int   used;
	    int   maxlen;

	    maxlen = MAX_METHOD_NAME_LENGTH;
	    if ( gdata->exclude == NULL ) {
		gdata->exclude = (char*)calloc(maxlen+1, 1);
		used = 0;
	    } else {
		used  = (int)strlen(gdata->exclude);
		gdata->exclude[used++] = ',';
		gdata->exclude[used] = 0;
		gdata->exclude = (char*)
			     realloc((void*)gdata->exclude, used+maxlen+1);
	    }
	    if ( gdata->exclude == NULL ) {
		fatal_error("ERROR: Out of malloc memory\n");
	    }
	    /* Add this item to the list */
	    next = get_token(next, ",=", gdata->exclude+used, maxlen);
	    /* Check for token scan error */
	    if ( next==NULL ) {
		fatal_error("ERROR: exclude option error\n");
	    }
	} else if ( token[0]!=0 ) {
	    /* We got a non-empty token and we don't know what it is. */
	    fatal_error("ERROR: Unknown option: %s\n", token);
	}
	/* Get the next token (returns NULL if there are no more) */
        next = get_token(next, ",=", token, sizeof(token));
    }
}

/* Agent_OnLoad: This is called immediately after the shared library is 
 *   loaded. This is the first code executed.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
    static GlobalAgentData data;
    jvmtiEnv              *jvmti;
    jvmtiError             error;
    jint                   res;
    jvmtiCapabilities      capabilities;
    jvmtiEventCallbacks    callbacks;
    
    /* Setup initial global agent data area 
     *   Use of static/extern data should be handled carefully here.
     *   We need to make sure that we are able to cleanup after ourselves
     *     so anything allocated in this library needs to be freed in
     *     the Agent_OnUnload() function.
     */
    (void)memset((void*)&data, 0, sizeof(data));
    gdata = &data;
   
    /* First thing we need to do is get the jvmtiEnv* or JVMTI environment */
    res = (vm)->GetEnv((void **)&jvmti, JVMTI_VERSION_1);
    if (res != JNI_OK) {
	/* This means that the VM was unable to obtain this version of the
	 *   JVMTI interface, this is a fatal error.
	 */
	fatal_error("ERROR: Unable to access JVMTI Version 1 (0x%x),"
                " is your JDK a 5.0 or newer version?"
                " JNIEnv's GetEnv() returned %d\n",
               JVMTI_VERSION_1, res);
    }

    /* Here we save the jvmtiEnv* for Agent_OnUnload(). */
    gdata->jvmti = jvmti;
   
    /* Parse any options supplied on java command line */
    parse_agent_options(options);
   
     /* Immediately after getting the jvmtiEnv* we need to ask for the
     *   capabilities this agent will need. In this case we need to make
     *   sure that we can get all class load hooks.
     */
    (void)memset(&capabilities,0, sizeof(capabilities));
    capabilities.can_generate_all_class_hook_events  = 1;
    error = (jvmti)->AddCapabilities(&capabilities);
    check_jvmti_error(jvmti, error, "Unable to get necessary JVMTI capabilities.");
    
    /* Next we need to provide the pointers to the callback functions to
     *   to this jvmtiEnv*
     */
    (void)memset(&callbacks,0, sizeof(callbacks));
    /* JVMTI_EVENT_VM_START */
    callbacks.VMStart           = &cbVMStart;      
    /* JVMTI_EVENT_VM_INIT */
    callbacks.VMInit           = &cbVMInit;      
    /* JVMTI_EVENT_VM_DEATH */
    callbacks.VMDeath           = &cbVMDeath;     
    /* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
    callbacks.ClassFileLoadHook = &cbClassFileLoadHook; 
    error = (jvmti)->SetEventCallbacks(&callbacks, (jint)sizeof(callbacks));
    check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");
   
    /* At first the only initial events we are interested in are VM
     *   initialization, VM death, and Class File Loads. 
     *   Once the VM is initialized we will request more events.
     */
    error = (jvmti)->SetEventNotificationMode(JVMTI_ENABLE, 
			  JVMTI_EVENT_VM_START, (jthread)NULL);
    check_jvmti_error(jvmti, error, "Cannot set event notification");
    error = (jvmti)->SetEventNotificationMode(JVMTI_ENABLE, 
			  JVMTI_EVENT_VM_INIT, (jthread)NULL);
    check_jvmti_error(jvmti, error, "Cannot set event notification");
    error = (jvmti)->SetEventNotificationMode(JVMTI_ENABLE, 
			  JVMTI_EVENT_VM_DEATH, (jthread)NULL);
    check_jvmti_error(jvmti, error, "Cannot set event notification");
    error = (jvmti)->SetEventNotificationMode(JVMTI_ENABLE, 
			  JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL);
    check_jvmti_error(jvmti, error, "Cannot set event notification");
   
    /* Here we create a raw monitor for our use in this agent to
     *   protect critical sections of code.
     */
    error = (jvmti)->CreateRawMonitor("agent data", &(gdata->lock));
    check_jvmti_error(jvmti, error, "Cannot create raw monitor");

    /* Add demo jar file to boot classpath */
    add_demo_jar_to_bootclasspath(jvmti, "minst");

    /* We return JNI_OK to signify success */
    return JNI_OK;
}

/* Agent_OnUnload: This is called immediately before the shared library is 
 *   unloaded. This is the last code executed.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    /* Make sure all malloc/calloc/strdup space is freed */
    if ( gdata->include != NULL ) {

		(void)free((void*)gdata->include);
	    gdata->include = NULL;
    }
    if ( gdata->exclude != NULL ) {

		(void)free((void*)gdata->exclude);
	     gdata->exclude = NULL;
    }
}

 

4、JAVA程序

     

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

import javax.net.ssl.ManagerFactoryParameters;

import com.unittest.conf.ConfigModel;
import com.unittest.util.ki;
import com.unittest.util.sd;

/*
 *作者:山人
 *
 * 输出JVM代码执行记录
 *
 */
public class JVMUnitTest {
	/* Master switch that activates methods. */
	private static int engaged = 0;
	public static void method_entry(int cnum, int mnum) {
		Class x = JVMUnitTest.class; 
		synchronized (x) {
			if (engaged > 0) {
				engaged = 0;
				String className = "Unknown";
				String methodName = "Unknown";
				int lineNumber=0;
				Exception exp = new Exception();
				StackTraceElement[] stack = exp.getStackTrace();
				if (stack.length > 1) {
					StringBuffer sb = new StringBuffer();
					StackTraceElement location = stack[1];
					className = location.getClassName();
					methodName = location.getMethodName();
					lineNumber=location.getLineNumber();
					Date date = new Date();
					SimpleDateFormat sdf = new SimpleDateFormat(
							"yyyy-MM-dd HH:mm:ss");
					String execDate = sdf.format(date);
					ConfigModel cm = JVMUnitTest.readerConfig();
					List<String> vmArgment = ManagementFactory.getRuntimeMXBean().getInputArguments();
					String agentLibPath = null;
					String datFilePath = null;
					for (int i = 0; i < vmArgment.size(); i++) {
						String agentLib = vmArgment.get(i);
						if (agentLib != null
								&& agentLib.indexOf("-agentlib") != -1) {
							agentLibPath = agentLib;
							break;
						}
					}
					if (agentLibPath != null) {
						int firstIndex=agentLibPath.indexOf(":");
						int lastIndex=agentLibPath.lastIndexOf("\\");
						if(firstIndex!=-1&&lastIndex!=-1)
						{
							datFilePath=agentLibPath.substring(firstIndex+1, lastIndex);
						}
					}
					File file = new File(datFilePath + "/" + cm.getDevName()
							+ "-" + cm.getProjectName() + "-" + cm.getVersion()
							+ ".dat");
					if (!file.exists()) {
						try {
							file.createNewFile();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
					sb.append("[" + execDate + "] [" + cm.getProjectName()
							+ "] [" + cm.getAuth() + "] " + "[" + className
							+ "] [" + methodName + "]\r\n");
					try {
						String text=sd.byte2hex(sb.toString().getBytes());
						ki.method1(file,text);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}

				engaged++;
			}
		}
	}

	private static ConfigModel readerConfig() {
		System.getenv();
		ConfigModel configMoel = new ConfigModel();
		try {
			configMoel.setAuth(System.getProperty("auth"));
			configMoel.setVersion(System.getProperty("version"));
			configMoel.setProjectName(System.getProperty("project"));
			configMoel.setDevName(System.getProperty("devName"));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return configMoel;
	}

}

 

完整的程序请到我的空间里下载

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lishengbo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值