码迷,mamicode.com
首页 > 编程语言 > 详细

不基于框架(spring) 的方法监控技术

时间:2017-08-25 16:55:06      阅读:232      评论:0      收藏:0      [点我收藏+]

标签:cto   macros   constant   media   work   elements   php   ref   sig   

问题描述

为了方便对Java代码执行效率调优,目前已开发了基于spring aop方式的各函数执行监控。 但其缺点是必须依赖于spring,对于非spring管控的对象则无法监控。

期待解决思路

期望可以开发出不依赖于spring的监控,可考虑从classloader 、jvm 、jmx等底层技术去实现不依赖于第三方框架的解决方案

问题解决责任人

问题提出者:苏印
问题解决责任人:苏印 王加平 王伟 余波

Create by Ian.Su 20170804

解决方案如下

技术方案: JVM TI

详细介绍参考:http://blog.csdn.net/lishengbo/article/details/40660091

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技

术,我们可以实现如下功能: 了解程序的执行过程 监控程序的执行性能 调试你的多线程程序 开发JAVA的自动化测试工具 开发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. "" * 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. "" * 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. "" * 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 
#include 
#include 
#include <java_crw_demo.h>
/* Get Java and class file and bytecode information. */

#include

#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, FILELINE)

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

#define CRW_ASSERT(ci, cond) \
((cond)?(void)0:assert_error(ci, #cond, FILELINE))

#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&x7F) ) {  
    bytecodes[nbytes++] = (ByteCode)JVM_OPC_ldc;  
} else {  
    bytecodes[nbytes++] = (ByteCode)JVM_OPC_ldc_w;  
    bytecodes[nbytes++] = (ByteCode)((index >> 8) & xFF);   
}  
bytecodes[nbytes++] = (ByteCode)(index & xFF);  
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&x7F) ) {  
    bytecodes[nbytes++] = (ByteCode)JVM_OPC_bipush;  
    bytecodes[nbytes++] = (ByteCode)(number & xFF);  
} else {  
    bytecodes[nbytes++] = (ByteCode)JVM_OPC_sipush;  
    bytecodes[nbytes++] = (ByteCode)((number >> 8) & xFF);   
    bytecodes[nbytes++] = (ByteCode)(number & xFF);  
}  
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,"")==0 ) {
return JNI_TRUE;
}
return JNI_FALSE;
}

static int
is_clinit_method(const char *name)
{
if ( name!=NULL && strcmp(name,"")==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; icpool_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 */
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 
#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 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;  
}

}

不基于框架(spring) 的方法监控技术

标签:cto   macros   constant   media   work   elements   php   ref   sig   

原文地址:http://www.cnblogs.com/liushuncheng/p/7428491.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!