ij-16.png   inspectorJ -- JavaTM Profiler
sf project site browse source checkout source
SourceForge.net Logo



src/inspectorj/agent/inspectorjagent.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   inspectorJ - java profiler                                            *
00003  *   Copyright (C) 2007 by James May
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  *                                                                         *
00010  *   This program is distributed in the hope that it will be useful,       *
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00013  *   GNU General Public License for more details.                          *
00014  *                                                                         *
00015  *   You should have received a copy of the GNU General Public License     *
00016  *   along with this program; if not, write to the                         *
00017  *   Free Software Foundation, Inc.,                                       *
00018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00019  ***************************************************************************/
00020         
00026 #include "inspectorj/agent/inspectorjagent.h"
00027 
00028 
00029 using namespace inspectorj::agent;
00030 using inspectorj::client::SessionProfile;
00031 using inspectorj::toolset::ProfileToolSet;
00032 
00033 /* ------------------------------------------------------------------- */
00034 /* Some constant maximum sizes */
00035 
00036 #define MAX_TOKEN_LENGTH    256              
00037 #define MAX_THREAD_NAME_LENGTH  512         
00038 #define MAX_METHOD_NAME_LENGTH  1024        
00039 #define INSPECTORJ_class        Mtrace      
00040 #define INSPECTORJ_entry        method_entry    
00041 #define INSPECTORJ_exit         method_exit     
00042 #define INSPECTORJ_native_entry _method_entry   
00043 #define INSPECTORJ_native_exit  _method_exit    
00044 #define INSPECTORJ_engaged      engaged         
00045 
00046 /* C macros to create strings from tokens */
00047 #define _STRING(s) #s
00048 #define STRING(s) _STRING(s)
00049 
00050 /* ------------------------------------------------------------------- */
00051 
00053 typedef struct MethodInfo {
00054     const char *name;          
00055     const char *signature;     
00056     int         calls;         
00057     int         returns;       
00058 } MethodInfo;
00059 
00061 typedef struct ClassInfo {
00062     const char *name;          
00063     int         mcount;        
00064     MethodInfo *methods;       
00065     int         calls;         
00066 } ClassInfo;
00067 
00068 
00073 typedef struct {
00074     /* JVMTI Environment */
00075     JavaVM        *vm;
00076     JNIEnv        *env;
00077     jvmtiEnv      *jvmti;
00078     jboolean       vm_is_dead;
00079     jboolean       vm_is_started;
00080     /* Data access Lock */
00081     jrawMonitorID  lock;
00082     /* Options */
00083     SessionProfile *profile;
00084     QList<QRegExp> *regexFilterList;
00085 
00086     /* ClassInfo Table */
00087     ClassInfo      *classes;
00088     jint            ccount;
00089     
00090     /* Agent Server */
00091     AgentServer    *server;
00092     AgentPacketHandler *cmdHandler;
00093     boost::thread *serverThread;
00094 } AgentGlobalContext;
00095 
00096 static AgentGlobalContext *context;
00097 
00098 
00099 
00104 static void
00105 deallocate(jvmtiEnv *jvmti, void *ptr)
00106 {
00107     jvmtiError error;
00108 
00109     error = jvmti->Deallocate(static_cast<unsigned char *>(ptr));
00110     check_jvmti_error(jvmti, error, "Cannot deallocate memory");
00111 }
00112 
00116 static void *
00117 allocate(jvmtiEnv *jvmti, jint len)
00118 {
00119     jvmtiError error;
00120     void      *ptr;
00121 
00122     error = jvmti->Allocate(len, (unsigned char **)&ptr);
00123     check_jvmti_error(jvmti, error, "Cannot allocate memory");
00124     return ptr;
00125 }
00126 
00130 static void
00131 enter_critical_section(jvmtiEnv *jvmti)
00132 {
00133     jvmtiError error;
00134 
00135     error = jvmti->RawMonitorEnter(context->lock);
00136     check_jvmti_error(jvmti, error, "Cannot enter with raw monitor");
00137 }
00138 
00142 static void
00143 exit_critical_section(jvmtiEnv *jvmti)
00144 {
00145     jvmtiError error;
00146 
00147     error = jvmti->RawMonitorExit(context->lock);
00148     check_jvmti_error(jvmti, error, "Cannot exit with raw monitor");
00149 }
00150 
00154 static void
00155 get_thread_name(jvmtiEnv *jvmti, jthread thread, char *tname, int maxlen)
00156 {
00157     jvmtiThreadInfo info;
00158     jvmtiError      error;
00159 
00160     /* Make sure the stack variables are garbage free */
00161     (void)memset(&info,0, sizeof(info));
00162 
00163     /* Assume the name is unknown for now */
00164     (void)strcpy(tname, "Unknown");
00165 
00166     /* Get the thread information, which includes the name */
00167     error = jvmti->GetThreadInfo(thread, &info);
00168     check_jvmti_error(jvmti, error, "Cannot get thread info");
00169 
00170     /* The thread might not have a name, be careful here. */
00171     if ( info.name != NULL ) {
00172     int len;
00173 
00174     /* Copy the thread name into tname if it will fit */
00175     len = (int)strlen(info.name);
00176     if ( len < maxlen ) {
00177         (void)strcpy(tname, info.name);
00178     }
00179 
00180     /* Every string allocated by JVMTI needs to be freed */
00181     deallocate(jvmti, (void*)info.name);
00182     }
00183 }
00184 
00188 static int
00189 class_compar(const void *e1, const void *e2)
00190 {
00191     ClassInfo *c1 = (ClassInfo*)e1;
00192     ClassInfo *c2 = (ClassInfo*)e2;
00193     if ( c1->calls > c2->calls ) return  1;
00194     if ( c1->calls < c2->calls ) return -1;
00195     return 0;
00196 }
00197 
00201 static int
00202 method_compar(const void *e1, const void *e2)
00203 {
00204     MethodInfo *m1 = (MethodInfo*)e1;
00205     MethodInfo *m2 = (MethodInfo*)e2;
00206     if ( m1->calls > m2->calls ) return  1;
00207     if ( m1->calls < m2->calls ) return -1;
00208     return 0;
00209 }
00210 
00214 static void
00215 mnum_callbacks(unsigned cnum, const char **names, const char**sigs, int mcount)
00216 {
00217     ClassInfo  *cp;
00218     int         mnum;
00219 
00220     if ( cnum >= (unsigned)context->ccount ) {
00221     AgentLogger::fatal("=> Class number out of range");
00222     }
00223     if ( mcount == 0 ) {
00224     return;
00225     }
00226 
00227     cp           = context->classes + (int)cnum;
00228     cp->calls    = 0;
00229     cp->mcount   = mcount;
00230     cp->methods  = (MethodInfo*)calloc(mcount, sizeof(MethodInfo));
00231     if ( cp->methods == NULL ) {
00232     AgentLogger::fatal("=> Out of malloc memory");
00233     }
00234 
00235     for ( mnum = 0 ; mnum < mcount ; mnum++ ) {
00236         MethodInfo *mp;
00237 
00238     mp            = cp->methods + mnum;
00239         mp->name      = (const char *)strdup(names[mnum]);
00240     if ( mp->name == NULL ) {
00241         AgentLogger::fatal("=> Out of malloc memory");
00242     }
00243         mp->signature = (const char *)strdup(sigs[mnum]);
00244     if ( mp->signature == NULL ) {
00245         AgentLogger::fatal("=> Out of malloc memory");
00246     }
00247     }
00248 }
00249 
00253 static void
00254 INSPECTORJ_native_entry(JNIEnv *env, jclass klass, jobject thread, jint cnum, jint mnum)
00255 {
00256     enter_critical_section(context->jvmti); {
00257 
00258     if ( !context->vm_is_dead ) {
00259             ClassInfo  *cp;
00260             MethodInfo *mp;
00261 
00262         if ( cnum >= context->ccount ) {
00263         AgentLogger::fatal("=> Class number out of range");
00264         }
00265             cp = context->classes + cnum;
00266         if ( mnum >= cp->mcount ) {
00267         AgentLogger::fatal("=> Method number out of range");
00268         }
00269         mp = cp->methods + mnum;
00270             if ( interested((char*)cp->name, (char*)mp->name,
00271                 context->regexFilterList)  ) {
00272         mp->calls++;
00273         cp->calls++;
00274         }
00275     }
00276     } exit_critical_section(context->jvmti);
00277 }
00278 
00282 static void
00283 INSPECTORJ_native_exit(JNIEnv *env, jclass klass, jobject thread, jint cnum, jint mnum)
00284 {
00285     enter_critical_section(context->jvmti); {
00286     /* It's possible we get here right after VmDeath event, be careful */
00287     if ( !context->vm_is_dead ) {
00288             ClassInfo  *cp;
00289             MethodInfo *mp;
00290 
00291         if ( cnum >= context->ccount ) {
00292         AgentLogger::fatal("=> Class number out of range");
00293         }
00294             cp = context->classes + cnum;
00295         if ( mnum >= cp->mcount ) {
00296         AgentLogger::fatal("=> Method number out of range");
00297         }
00298         mp = cp->methods + mnum;
00299             if ( interested((char*)cp->name, (char*)mp->name,
00300                         context->regexFilterList)  ) {
00301         mp->returns++;
00302         }
00303     }
00304     } exit_critical_section(context->jvmti);
00305 }
00306 
00310 static void JNICALL
00311 cbVMStart(jvmtiEnv *jvmti, JNIEnv *env)
00312 {
00313     enter_critical_section(jvmti); {
00314         jclass   klass;
00315     jfieldID field;
00316     int      rc;
00317 
00318     /* Java Native Methods for class */
00319     static JNINativeMethod registry[2] = {
00320         {STRING(INSPECTORJ_native_entry), "(Ljava/lang/Object;II)V",
00321         (void*)&INSPECTORJ_native_entry},
00322         {STRING(INSPECTORJ_native_exit),  "(Ljava/lang/Object;II)V",
00323         (void*)&INSPECTORJ_native_exit}
00324         };
00325 
00326     /* The VM has started. */
00327     AgentLogger::debug("VMStart");
00328 
00329     /* Register Natives for class whose methods we use */
00330     klass = env->FindClass(STRING(INSPECTORJ_class));
00331     if ( klass == NULL ) {
00332         AgentLogger::fatal("=> JNI: Cannot find %s with FindClass",
00333             STRING(INSPECTORJ_class));
00334     }
00335     rc = env->RegisterNatives(klass, registry, 2);
00336     if ( rc != 0 ) {
00337         AgentLogger::fatal("=> JNI: Cannot register native methods for %s",
00338             STRING(INSPECTORJ_class));
00339     }
00340 
00341     /* Engage calls. */
00342     field = env->GetStaticFieldID(klass, STRING(INSPECTORJ_engaged), "I");
00343     if ( field == NULL ) {
00344         AgentLogger::fatal("=> JNI: Cannot get field from %s",
00345             STRING(INSPECTORJ_class));
00346     }
00347     env->SetStaticIntField(klass, field, 1);
00348 
00349         /* Indicate VM has started */
00350         context->vm_is_started = JNI_TRUE;
00351 
00352     } exit_critical_section(jvmti);
00353 }
00354 
00355 
00359 void startServer()
00360 {
00361     AgentLogger::print("*****************************\n");
00362     AgentLogger::print("  inspectorj server starting\n");
00363     AgentLogger::print("  listening on port %s\n",
00364             context->profile->getConnection().port.toLocal8Bit().data());
00365     AgentLogger::print("*****************************\n");    
00366     context->server->start();
00367 }
00368 
00372 static void JNICALL
00373 cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
00374 {
00375     enter_critical_section(jvmti); {
00376     char  tname[MAX_THREAD_NAME_LENGTH];
00377     static jvmtiEvent events[] =
00378         { JVMTI_EVENT_THREAD_START, JVMTI_EVENT_THREAD_END };
00379     int        i;
00380 
00381     /* The VM has started. */
00382     get_thread_name(jvmti, thread, tname, sizeof(tname));
00383     AgentLogger::debug("VMInit %s", tname);
00384 
00385     /* The VM is now initialized, at this time we make our requests
00386      *   for additional events.
00387      */
00388 
00389     for( i=0; i < (int)(sizeof(events)/sizeof(jvmtiEvent)); i++) {
00390         jvmtiError error;
00391 
00392         /* Setup event  notification modes */
00393         error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
00394                   events[i], (jthread)NULL);
00395         check_jvmti_error(jvmti, error, "Cannot set event notification");
00396     }
00397     
00398     } exit_critical_section(jvmti);
00399     
00400     context->env = env;
00401     
00402     /* Create the agent server and store in global data */
00403     AgentPacketHandler *cmdHandler = new AgentPacketHandler(context->vm, context->env, context->jvmti);    
00404     AgentServer *server = new AgentServer(
00405             context->profile->getConnection().port.toInt(),
00406             cmdHandler);
00407     
00408     context->server = server;
00409     context->cmdHandler = cmdHandler;    
00410     // Start the agent server
00411     context->serverThread = &boost::thread(startServer);
00412 }
00413 
00417 static void JNICALL
00418 cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env)
00419 {
00420     enter_critical_section(jvmti); {
00421         jclass   klass;
00422     jfieldID field;
00423 
00424     /* The VM has died. */
00425     AgentLogger::info("VMDeath");
00426 
00427     /* Disengage calls in INSPECTORJ_class. */
00428     klass = env->FindClass(STRING(INSPECTORJ_class));
00429     if ( klass == NULL ) {
00430         AgentLogger::fatal("=> JNI: Cannot find %s with FindClass",
00431             STRING(INSPECTORJ_class));
00432     }
00433     field = env->GetStaticFieldID(klass, STRING(INSPECTORJ_engaged), "I");
00434     if ( field == NULL ) {
00435         AgentLogger::fatal("=> JNI: Cannot get field from %s",
00436             STRING(INSPECTORJ_class));
00437     }
00438     env->SetStaticIntField(klass, field, 0);
00439 
00440     /* The critical section here is important to hold back the VM death
00441      *    until all other callbacks have completed.
00442      */
00443 
00444     /* Since this critical section could be holding up other threads
00445      *   in other event callbacks, we need to indicate that the VM is
00446      *   dead so that the other callbacks can short circuit their work.
00447      *   We don't expect any further events after VmDeath but we do need
00448      *   to be careful that existing threads might be in our own agent
00449      *   callback code.
00450      */
00451     context->vm_is_dead = JNI_TRUE;
00452 
00453     /* Dump out stats */
00454     AgentLogger::info("Begin Class Stats");
00455     if ( context->ccount > 0 ) {
00456         int cnum;
00457 
00458         /* Sort table (in place) by number of method calls into class. */
00459         /*  Note: Do not use this table after this qsort! */
00460         qsort(context->classes, context->ccount, sizeof(ClassInfo),
00461             &class_compar);
00462 
00463         /* Dump out context->max_count most called classes */
00464         for ( cnum=context->ccount-1 ;
00465           cnum >= 0 && cnum >= context->ccount - 4;
00466           cnum-- ) {
00467         ClassInfo *cp;
00468             int        mnum;
00469 
00470         cp = context->classes + cnum;
00471         AgentLogger::info("Class %s %d calls", cp->name, cp->calls);
00472         if ( cp->calls==0 ) continue;
00473 
00474         /* Sort method table (in place) by number of method calls. */
00475         /*  Note: Do not use this table after this qsort! */
00476         qsort(cp->methods, cp->mcount, sizeof(MethodInfo),
00477                 &method_compar);
00478         for ( mnum=cp->mcount-1 ; mnum >= 0 ; mnum-- ) {
00479             MethodInfo *mp;
00480 
00481             mp = cp->methods + mnum;
00482             if ( mp->calls==0 ) continue;
00483             AgentLogger::info("\tMethod %s %s %d calls %d returns",
00484             mp->name, mp->signature, mp->calls, mp->returns);
00485         }
00486         }
00487     }
00488     AgentLogger::info("End Class Stats");
00489     (void)fflush(stdout);
00490 
00491     } exit_critical_section(jvmti);
00492 
00493 }
00494 
00498 static void JNICALL
00499 cbThreadStart(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
00500 {
00501     enter_critical_section(jvmti); {
00502     /* It's possible we get here right after VmDeath event, be careful */
00503     if ( !context->vm_is_dead ) {
00504         char  tname[MAX_THREAD_NAME_LENGTH];
00505 
00506         get_thread_name(jvmti, thread, tname, sizeof(tname));
00507         AgentLogger::info("ThreadStart %s", tname);
00508     }
00509     } exit_critical_section(jvmti);
00510 }
00511 
00515 static void JNICALL
00516 cbThreadEnd(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
00517 {
00518     enter_critical_section(jvmti); {
00519     /* It's possible we get here right after VmDeath event, be careful */
00520     if ( !context->vm_is_dead ) {
00521         char  tname[MAX_THREAD_NAME_LENGTH];
00522 
00523         get_thread_name(jvmti, thread, tname, sizeof(tname));
00524         AgentLogger::info("ThreadEnd %s", tname);
00525     }
00526     } exit_critical_section(jvmti);
00527 }
00528 
00532 static void JNICALL
00533 cbClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env,
00534                 jclass class_being_redefined, jobject loader,
00535                 const char* name, jobject protection_domain,
00536                 jint class_data_len, const unsigned char* class_data,
00537                 jint* new_class_data_len, unsigned char** new_class_data)
00538 {
00539     enter_critical_section(jvmti); {
00540     /* It's possible we get here right after VmDeath event, be careful */
00541     if ( !context->vm_is_dead ) {
00542 
00543         const char *classname;
00544 
00545             /* Name could be NULL */
00546         if ( name == NULL ) {
00547         classname = java_crw_demo_classname(class_data, class_data_len,
00548             NULL);
00549         if ( classname == NULL ) {
00550             AgentLogger::fatal("=> No classname inside classfile");
00551         }
00552         } else {
00553         classname = strdup(name);
00554         if ( classname == NULL ) {
00555             AgentLogger::fatal("=> Out of malloc memory");
00556         }
00557         }
00558 
00559         *new_class_data_len = 0;
00560             *new_class_data     = NULL;
00561 
00562             /* The tracker class itself? */
00563             if ( interested((char*)classname, "", context->regexFilterList)
00564           &&  strcmp(classname, STRING(INSPECTORJ_class)) != 0 ) {
00565                 jint           cnum;
00566                 int            system_class;
00567                 unsigned char *new_image;
00568                 long           new_length;
00569         ClassInfo     *cp;
00570 
00571                 /* Get unique number for every class file image loaded */
00572                 cnum = context->ccount++;
00573 
00574         /* Save away class information */
00575         if ( context->classes == NULL ) {
00576             context->classes = (ClassInfo*)malloc(
00577                 context->ccount*sizeof(ClassInfo));
00578                 } else {
00579             context->classes = (ClassInfo*)
00580                 realloc((void*)context->classes,
00581                 context->ccount*sizeof(ClassInfo));
00582         }
00583         if ( context->classes == NULL ) {
00584             AgentLogger::fatal("=> Out of malloc memory");
00585         }
00586         cp           = context->classes + cnum;
00587         cp->name     = (const char *)strdup(classname);
00588         if ( cp->name == NULL ) {
00589             AgentLogger::fatal("=> Out of malloc memory");
00590         }
00591         cp->calls    = 0;
00592         cp->mcount   = 0;
00593         cp->methods  = NULL;
00594 
00595         /* Is it a system class? If the class load is before VmStart
00596          *   then we will consider it a system class that should
00597          *   be treated carefully. (See java_crw_demo)
00598          */
00599                 system_class = 0;
00600                 if ( !context->vm_is_started ) {
00601                     system_class = 1;
00602                 }
00603 
00604                 new_image = NULL;
00605                 new_length = 0;
00606 
00607                 /* Call the class file reader/write demo code */
00608                 java_crw_demo(cnum,
00609                     classname,
00610                     class_data,
00611                     class_data_len,
00612                     system_class,
00613                     STRING(INSPECTORJ_class), "L" STRING(INSPECTORJ_class) ";",
00614                     STRING(INSPECTORJ_entry), "(II)V",
00615                     STRING(INSPECTORJ_exit), "(II)V",
00616                     NULL, NULL,
00617                     NULL, NULL,
00618                     &new_image,
00619                     &new_length,
00620                     NULL,
00621                     &mnum_callbacks);
00622 
00623         /* If we got back a new class image, return it back as "the"
00624          *   new class image. This must be JVMTI Allocate space.
00625          */
00626                 if ( new_length > 0 ) {
00627                     unsigned char *jvmti_space;
00628 
00629                     jvmti_space = (unsigned char *)allocate(jvmti, (jint)new_length);
00630                     (void)memcpy((void*)jvmti_space, (void*)new_image, (int)new_length);
00631                     *new_class_data_len = (jint)new_length;
00632                     *new_class_data     = jvmti_space; /* VM will deallocate */
00633                 }
00634 
00635                 /* Always free up the space we get from java_crw_demo() */
00636                 if ( new_image != NULL ) {
00637                     (void)free((void*)new_image); /* Free malloc() space with free() */
00638                 }
00639             }
00640         (void)free((void*)classname);
00641     }
00642     } exit_critical_section(jvmti);
00643 }
00644 
00648 static void parseOptions(char *options)
00649 {
00650     char token[MAX_TOKEN_LENGTH];
00651     char *next;
00652 
00653     /* Parse options and set flags in context */
00654     if ( options == NULL ) {
00655         options = "help";
00656     }
00657     
00658     /* Get the first token from the options string. */
00659     next = getToken(options, ",=", token, sizeof(token));
00660 
00661     /* While not at the end of the options string, process this option. */
00662     while ( next != NULL ) {
00663     if ( strcmp(token,"help")==0 ) {
00664         AgentLogger::println("");
00665         AgentLogger::println("====================");
00666         AgentLogger::println("inspectorJ JVM Agent");
00667         AgentLogger::println("====================");
00668         AgentLogger::println("");
00669         AgentLogger::println(" java -agent:ij_agent=profile=<path_to_profile>[,=options,...]");
00670         AgentLogger::println("");
00671         AgentLogger::println("\t port=xxxx\txxxx is the port number used by the agent server");
00672         AgentLogger::println("");        
00673         AgentLogger::println(" The options are comma separated:");
00674         AgentLogger::println("\t help\t\tPrint help information");
00675         AgentLogger::println("\t loglevel=n\tlogging [0 = error, 1 = warn, (2) = info]");
00676         AgentLogger::println("");
00677         exit(0);
00678     } else if ( strcmp(token,"loglevel")==0 ) {
00679             char number[MAX_TOKEN_LENGTH];
00680 
00681         /* Get the numeric option */
00682         next = getToken(next, ",=", number, (int)sizeof(number));
00683         /* Check for token scan error */
00684         if ( next==NULL ) {
00685             AgentLogger::error("=> loglevel=n option error");
00686             next = "1";
00687         }
00688         /* Save numeric value */        
00689         AgentLogger::loglevel = atoi(number);         
00690     } else if ( strcmp(token,"profile")==0 ) {
00691             char fileName[MAX_TOKEN_LENGTH];
00692 
00693         /* Get the numeric option */
00694         next = getToken(next, ",=", fileName, (int)sizeof(fileName));
00695         
00696         /* Check for token scan error */
00697         if ( next==NULL ) {
00698         AgentLogger::fatal("=> profile option error");
00699         }
00700         /* Save session profile */
00701         context->profile = new SessionProfile(); 
00702         if (!ProfileToolSet::loadProfile(*(context->profile), fileName)) {
00703             AgentLogger::fatal("=> Could not load profile: %s", fileName);
00704         }
00705         context->regexFilterList = new QList<QRegExp>();
00706 
00707         // create class filter regular expressions
00708         if (context->profile) {
00709             QStringList filters = context->profile->getClassFilters();
00710             QStringListIterator filterIter(filters);
00711             while(filterIter.hasNext()) {
00712                 QRegExp regexp;
00713                 createAgentClassFilter(regexp, filterIter.next());
00714                 *(context->regexFilterList) << regexp;
00715             }
00716         }
00717     } else if ( token[0]!=0 ) {
00718         /* We got a non-empty token and we don't know what it is. */
00719         AgentLogger::fatal("=> Unknown option: %s", token);
00720     }
00721     /* Get the next token (returns NULL if there are no more) */
00722         next = getToken(next, ",=", token, sizeof(token));
00723     }        
00724 }
00725 
00730 JNIEXPORT jint JNICALL
00731 Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
00732 {
00733     static AgentGlobalContext ctx;
00734     jvmtiEnv              *jvmti;
00735     jvmtiError             error;
00736     jint                   res;
00737     jvmtiCapabilities      capabilities;
00738     jvmtiEventCallbacks    callbacks;
00739 
00740     /* Setup initial global agent data area
00741      *   Use of static/extern data should be handled carefully here.
00742      *   We need to make sure that we are able to cleanup after ourselves
00743      *     so anything allocated in this library needs to be freed in
00744      *     the Agent_OnUnload() function.
00745      */
00746     (void)memset((void*)&ctx, 0, sizeof(ctx));
00747     context = &ctx;
00748 
00749     /* First thing we need to do is get the jvmtiEnv* or JVMTI environment */
00750     res = vm->GetEnv((void **)&jvmti, JVMTI_VERSION_1);
00751     if (res != JNI_OK) {
00752     /* This means that the VM was unable to obtain this version of the
00753      *   JVMTI interface, this is a fatal error.
00754      */
00755     AgentLogger::fatal("=> Unable to access JVMTI Version 1 (0x%x),"
00756                 " is your J2SE a 1.5 or newer version?"
00757                 " JNIEnv's GetEnv() returned %d",
00758                JVMTI_VERSION_1, res);
00759     }
00760     
00761     context->vm = vm;
00762 
00763     /* Here we save the jvmtiEnv* for Agent_OnUnload(). */
00764     context->jvmti = jvmti;
00765 
00766     /* Parse any options supplied on the command line */
00767     parseOptions(options);
00768 
00769     /* Immediately after getting the jvmtiEnv* we need to ask for the
00770      *   capabilities this agent will need. In this case we need to make
00771      *   sure that we can get all class load hooks.
00772      */
00773     (void)memset(&capabilities,0, sizeof(capabilities));
00774     capabilities.can_generate_all_class_hook_events  = 1;
00775     capabilities.can_get_source_file_name = 1;
00776     capabilities.can_access_local_variables = 1;
00777     capabilities.can_get_line_numbers = 1;
00778     error = jvmti->AddCapabilities(&capabilities);
00779     check_jvmti_error(jvmti, error, "Unable to get necessary JVMTI capabilities.");
00780 
00781     /* Next we need to provide the pointers to the callback functions to
00782      *   to this jvmtiEnv*
00783      */
00784     (void)memset(&callbacks,0, sizeof(callbacks));
00785     /* JVMTI_EVENT_VM_START */
00786     callbacks.VMStart           = &cbVMStart;
00787     /* JVMTI_EVENT_VM_INIT */
00788     callbacks.VMInit            = &cbVMInit;
00789     /* JVMTI_EVENT_VM_DEATH */
00790     callbacks.VMDeath           = &cbVMDeath;
00791     /* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
00792     callbacks.ClassFileLoadHook = &cbClassFileLoadHook;
00793     /* JVMTI_EVENT_THREAD_START */
00794     callbacks.ThreadStart       = &cbThreadStart;
00795     /* JVMTI_EVENT_THREAD_END */
00796     callbacks.ThreadEnd         = &cbThreadEnd;
00797     error = jvmti->SetEventCallbacks(&callbacks, (jint)sizeof(callbacks));
00798     check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");
00799 
00800     /* At first the only initial events we are interested in are VM
00801      *   initialization, VM death, and Class File Loads.
00802      *   Once the VM is initialized we will request more events.
00803      */
00804     error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
00805               JVMTI_EVENT_VM_START, (jthread)NULL);
00806     check_jvmti_error(jvmti, error, "Cannot set event notification");
00807     error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
00808               JVMTI_EVENT_VM_INIT, (jthread)NULL);
00809     check_jvmti_error(jvmti, error, "Cannot set event notification");
00810     error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
00811               JVMTI_EVENT_VM_DEATH, (jthread)NULL);
00812     check_jvmti_error(jvmti, error, "Cannot set event notification");
00813     error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
00814               JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL);
00815     check_jvmti_error(jvmti, error, "Cannot set event notification");
00816 
00817     /* Here we create a raw monitor for our use in this agent to
00818      *   protect critical sections of code.
00819      */
00820     error = jvmti->CreateRawMonitor("inspectorj agent", &(context->lock));
00821     check_jvmti_error(jvmti, error, "Cannot create raw monitor");    
00822     
00823     /* We return JNI_OK to signify success */
00824     return JNI_OK;
00825 }
00826 
00831 JNIEXPORT void JNICALL
00832 Agent_OnUnload(JavaVM *vm)
00833 {
00834     /* Make sure all malloc/calloc/strdup space is freed */
00835 
00836     // stop and delete the server instance
00837     if ( context->server != NULL ) {
00838         AgentLogger::print("**********************************\n");
00839         AgentLogger::print("  shutting down inspectorj server\n");                   
00840             
00841         // stop the server
00842         context->server->stop();
00843         
00844         // wait for server to stop...
00845         while(context->server->isStarted()) { }       
00846         AgentLogger::print("  unloading inspectorj agent\n"); 
00847     
00848         delete context->server;
00849         context->server = NULL;
00850     }
00851     
00852     // delete the AgentPacketHandler instance
00853     if ( context->cmdHandler != NULL ) {
00854         delete context->cmdHandler;
00855         context->cmdHandler = NULL;
00856     }
00857 
00858     // delete the regexFilterList
00859     if ( context->regexFilterList != NULL ) {
00860         delete context->regexFilterList;
00861         context->regexFilterList = NULL;
00862     }
00863           
00864     // delete the SessionProfile
00865     if ( context->profile != NULL ) {
00866         delete context->profile;
00867         context->profile = NULL;
00868     }
00869 
00870     if ( context->classes != NULL ) {
00871         int cnum;      
00872 
00873         for ( cnum = 0 ; cnum < context->ccount ; cnum++ ) {
00874             ClassInfo *cp;    
00875             cp = context->classes + cnum;
00876             (void)free((void*)cp->name);
00877             
00878             if ( cp->mcount > 0 ) {
00879                 int mnum;
00880                 for ( mnum = 0 ; mnum < cp->mcount ; mnum++ ) {
00881                     MethodInfo *mp;        
00882                     mp = cp->methods + mnum;
00883                     (void)free((void*)mp->name);
00884                     (void)free((void*)mp->signature);
00885                 }
00886                 (void)free((void*)cp->methods);
00887             }
00888         }
00889         
00890         (void)free((void*)context->classes);
00891         context->classes = NULL;
00892     }
00893     AgentLogger::print("**********************************\n");
00894 }
00895 

Generated on Sun Aug 19 17:07:53 2007 for inspectorJ by  doxygen 1.5.1