1. 程式人生 > >使用JVM TI技術插裝位元組碼,實現Java程式碼執行控監Agent

使用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, read