| /* |
| * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * - Redistributions 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 Oracle nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <agent_util.h> |
| |
| /* ------------------------------------------------------------------- */ |
| /* Generic C utility functions */ |
| |
| /* Send message to stdout or whatever the data output location is */ |
| void |
| stdout_message(const char * format, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, format); |
| (void)vfprintf(stdout, format, ap); |
| va_end(ap); |
| } |
| |
| /* Send message to stderr or whatever the error output location is and exit */ |
| void |
| fatal_error(const char * format, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, format); |
| (void)vfprintf(stderr, format, ap); |
| (void)fflush(stderr); |
| va_end(ap); |
| exit(3); |
| } |
| |
| /* Get a token from a string (strtok is not MT-safe) |
| * str String to scan |
| * seps Separation characters |
| * buf Place to put results |
| * max Size of buf |
| * Returns NULL if no token available or can't do the scan. |
| */ |
| char * |
| get_token(char *str, char *seps, char *buf, int max) |
| { |
| int len; |
| |
| buf[0] = 0; |
| if ( str==NULL || str[0]==0 ) { |
| return NULL; |
| } |
| str += strspn(str, seps); |
| if ( str[0]==0 ) { |
| return NULL; |
| } |
| len = (int)strcspn(str, seps); |
| if ( len >= max ) { |
| return NULL; |
| } |
| (void)strncpy(buf, str, len); |
| buf[len] = 0; |
| return str+len; |
| } |
| |
| /* Determines if a class/method is specified by a list item |
| * item String that represents a pattern to match |
| * If it starts with a '*', then any class is allowed |
| * If it ends with a '*', then any method is allowed |
| * cname Class name, e.g. "java.lang.Object" |
| * mname Method name, e.g. "<init>" |
| * Returns 1(true) or 0(false). |
| */ |
| static int |
| covered_by_list_item(char *item, char *cname, char *mname) |
| { |
| int len; |
| |
| len = (int)strlen(item); |
| if ( item[0]=='*' ) { |
| if ( strncmp(mname, item+1, len-1)==0 ) { |
| return 1; |
| } |
| } else if ( item[len-1]=='*' ) { |
| if ( strncmp(cname, item, len-1)==0 ) { |
| return 1; |
| } |
| } else { |
| int cname_len; |
| |
| cname_len = (int)strlen(cname); |
| if ( strncmp(cname, item, (len>cname_len?cname_len:len))==0 ) { |
| if ( cname_len >= len ) { |
| /* No method name supplied in item, we must have matched */ |
| return 1; |
| } else { |
| int mname_len; |
| |
| mname_len = (int)strlen(mname); |
| item += cname_len+1; |
| len -= cname_len+1; |
| if ( strncmp(mname, item, (len>mname_len?mname_len:len))==0 ) { |
| return 1; |
| } |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /* Determines if a class/method is specified by this list |
| * list String of comma separated pattern items |
| * cname Class name, e.g. "java.lang.Object" |
| * mname Method name, e.g. "<init>" |
| * Returns 1(true) or 0(false). |
| */ |
| static int |
| covered_by_list(char *list, char *cname, char *mname) |
| { |
| char token[1024]; |
| char *next; |
| |
| if ( list[0] == 0 ) { |
| return 0; |
| } |
| |
| next = get_token(list, ",", token, sizeof(token)); |
| while ( next != NULL ) { |
| if ( covered_by_list_item(token, cname, mname) ) { |
| return 1; |
| } |
| next = get_token(next, ",", token, sizeof(token)); |
| } |
| return 0; |
| } |
| |
| /* Determines which class and methods we are interested in |
| * cname Class name, e.g. "java.lang.Object" |
| * mname Method name, e.g. "<init>" |
| * include_list Empty or an explicit list for inclusion |
| * exclude_list Empty or an explicit list for exclusion |
| * Returns 1(true) or 0(false). |
| */ |
| int |
| interested(char *cname, char *mname, char *include_list, char *exclude_list) |
| { |
| if ( exclude_list!=NULL && exclude_list[0]!=0 && |
| covered_by_list(exclude_list, cname, mname) ) { |
| return 0; |
| } |
| if ( include_list!=NULL && include_list[0]!=0 && |
| !covered_by_list(include_list, cname, mname) ) { |
| return 0; |
| } |
| return 1; |
| } |
| |
| /* ------------------------------------------------------------------- */ |
| /* Generic JVMTI utility functions */ |
| |
| /* Every JVMTI interface returns an error code, which should be checked |
| * to avoid any cascading errors down the line. |
| * The interface GetErrorName() returns the actual enumeration constant |
| * name, making the error messages much easier to understand. |
| */ |
| void |
| check_jvmti_error(jvmtiEnv *jvmti, jvmtiError errnum, const char *str) |
| { |
| if ( errnum != JVMTI_ERROR_NONE ) { |
| char *errnum_str; |
| |
| errnum_str = NULL; |
| (void)(*jvmti)->GetErrorName(jvmti, 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 |
| deallocate(jvmtiEnv *jvmti, void *ptr) |
| { |
| jvmtiError error; |
| |
| error = (*jvmti)->Deallocate(jvmti, 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(jvmti, 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(jvmti, "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(jvmti, (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(jvmti, (const char*)jar_path); |
| check_jvmti_error(jvmti, error, "Cannot add to boot classpath"); |
| } |
| |
| /* ------------------------------------------------------------------- */ |