1 /*
   2  * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 #include <string.h>
  24 #include <stdlib.h>
  25 #include <jni.h>
  26 #include <jni_tools.h>
  27 #include <nsk_tools.h>
  28 #include <aod.h>
  29 
  30 extern "C" {
  31 
  32 static volatile int internalError = 0;
  33 
  34 /*
  35  * This function can be used to inform AOD framework that some non critical for test logic
  36  * error happened inside shared function (e.g. JVMTI Deallocate failed).
  37  *
  38  * If this function was called status of all finishing AOD agents is changed to failed.
  39  */
  40 
  41 void nsk_aod_internal_error() {
  42     NSK_COMPLAIN0("WARNING: some error happened inside common function, see log for details\n");
  43     internalError = 1;
  44 }
  45 
  46 void nsk_free_options(Options* options) {
  47   if (options != NULL) {
  48     int i;
  49     for (i = 0; i < NSK_AOD_MAX_OPTIONS; i++) {
  50       if (options->names[i] != NULL) {
  51         free(options->names[i]);
  52       }
  53       if (options->values[i] != NULL) {
  54         free(options->values[i]);
  55       }
  56     }
  57     free(options);
  58   }
  59 }
  60 /*
  61  * Work with agent options
  62  */
  63 
  64 /*
  65  * Parse options and create structure Options
  66  */
  67 static Options* nsk_aod_createOptionsObject(char* optionsString) {
  68     int i = 0;
  69     Options* options;
  70     char* name;
  71     char* value;
  72     char* sep;
  73 
  74     if (optionsString == NULL) {
  75       NSK_COMPLAIN0("options were not passed to the native agent\n");
  76       return NULL;
  77     }
  78     options = (Options*) malloc(sizeof(Options));
  79     memset(options, 0, sizeof(Options));
  80     options->size = 0;
  81     name = optionsString;
  82     while (name != NULL && i < NSK_AOD_MAX_OPTIONS) {
  83       sep = strchr(name, '=');
  84       if (sep == NULL) { // name not found
  85         NSK_COMPLAIN1("Invalid options format: '%s'\n", optionsString);
  86         nsk_free_options(options);
  87         return NULL;
  88       }
  89       *sep = '\0';
  90       options->names[i] =  strdup(name);
  91       value = sep + 1;
  92       if (*value == '\0') { // value not found
  93         NSK_COMPLAIN1("Option '%s' is empty\n", options->names[i]);
  94         nsk_free_options(options);
  95         return NULL;
  96       }
  97       sep = strchr(value, ' ');
  98       if (sep != NULL) {
  99         *sep = '\0';
 100         name = sep + 1;
 101       } else {
 102         name = strchr(value, '\0');
 103       }
 104       options->values[i] = strdup(value);
 105       i++;
 106 
 107       if (*name == '\0') {
 108         name = NULL;
 109       }
 110     }
 111     if (name != NULL) {
 112       NSK_COMPLAIN1("WARNING: not all options were parsed, only %d options can be specified\n",
 113                     NSK_AOD_MAX_OPTIONS);
 114     }
 115     options->size = i;
 116     return options;
 117 }
 118 
 119 Options* nsk_aod_createOptions(char* optionsString) {
 120     Options* options;
 121 
 122     if (!NSK_VERIFY((options = (Options*) nsk_aod_createOptionsObject(optionsString)) != NULL))
 123         return NULL;
 124 
 125     if (!NSK_VERIFY(nsk_aod_optionSpecified(options, NSK_AOD_AGENT_NAME_OPTION))) {
 126         NSK_COMPLAIN0("Agent name wasn't specified\n");
 127         return NULL;
 128     }
 129 
 130     /*
 131      * verbose mode is true by default
 132      */
 133     nsk_setVerboseMode(NSK_TRUE);
 134 
 135     if (nsk_aod_optionSpecified(options, NSK_AOD_VERBOSE_OPTION)) {
 136         if (strcmp(nsk_aod_getOptionValue(options, NSK_AOD_VERBOSE_OPTION), "false") == 0)
 137             nsk_setVerboseMode(NSK_FALSE);
 138     }
 139 
 140     return options;
 141 }
 142 
 143 const char* nsk_aod_getOptionValue(Options* options, const char* option) {
 144     int i;
 145 
 146     if (!NSK_VERIFY(options != NULL)) {
 147         NSK_COMPLAIN0("Options NULL\n");
 148         return NULL;
 149     }
 150 
 151     for(i = 0; i < options->size; i++) {
 152         if (strcmp(option, options->names[i]) == 0) {
 153             return options->values[i];
 154         }
 155     }
 156 
 157     NSK_COMPLAIN1("Option '%s' isn't defined\n", option);
 158 
 159     return NULL;
 160 }
 161 
 162 int nsk_aod_optionSpecified(Options* options, const char* option) {
 163     int i;
 164 
 165     if (!NSK_VERIFY(options != NULL)) {
 166         NSK_COMPLAIN0("Options NULL\n");
 167         return NSK_FALSE;
 168     }
 169 
 170     for(i = 0; i < options->size; i++) {
 171         if (strcmp(option, options->names[i]) == 0) {
 172             return NSK_TRUE;
 173         }
 174     }
 175 
 176     return NSK_FALSE;
 177 }
 178 
 179 /*
 180  * Agent synchronization with target application
 181  */
 182 
 183 static const char* TARGET_APP_CLASS_NAME = "nsk/share/aod/TargetApplicationWaitingAgents";
 184 
 185 static const char* AGENT_LOADED_METHOD_NAME = "agentLoaded";
 186 static const char* AGENT_LOADED_METHOD_SIGNATURE = "(Ljava/lang/String;)V";
 187 
 188 static const char* AGENT_FINISHED_METHOD_NAME = "agentFinished";
 189 static const char* AGENT_FINISHED_METHOD_SIGNATURE = "(Ljava/lang/String;Z)V";
 190 
 191 static jclass targetAppClass = NULL;
 192 static jmethodID agentLoadedMethod = NULL;
 193 static jmethodID agentFinishedMethod = NULL;
 194 
 195 // this function is used to notify target application that native agent has been loaded
 196 int nsk_aod_agentLoaded(JNIEnv* jni, const char* agentName) {
 197     jstring agentNameString;
 198 
 199     NSK_DISPLAY1("Agent %s is loaded\n", agentName);
 200 
 201     if (targetAppClass == NULL) {
 202         /*
 203          * FindClass returns local reference, to cache reference to target application class
 204          * global reference should be created
 205          */
 206         jclass localTargetAppClass;
 207         if (!NSK_JNI_VERIFY(jni, (localTargetAppClass =
 208             NSK_CPP_STUB2(FindClass, jni, TARGET_APP_CLASS_NAME)) != NULL)) {
 209             return NSK_FALSE;
 210         }
 211 
 212         if (!NSK_JNI_VERIFY(jni, (targetAppClass = (jclass)
 213             NSK_CPP_STUB2(NewGlobalRef, jni, localTargetAppClass)) != NULL)) {
 214             return NSK_FALSE;
 215         }
 216     }
 217 
 218     if (agentLoadedMethod == NULL) {
 219         if (!NSK_JNI_VERIFY(jni, (agentLoadedMethod =
 220             NSK_CPP_STUB4(GetStaticMethodID, jni, targetAppClass,
 221                     AGENT_LOADED_METHOD_NAME, AGENT_LOADED_METHOD_SIGNATURE)) != NULL))
 222             return NSK_FALSE;
 223     }
 224 
 225     if (!NSK_JNI_VERIFY(jni, (agentNameString =
 226         NSK_CPP_STUB2(NewStringUTF, jni, agentName)) != NULL))
 227         return NSK_FALSE;
 228 
 229     NSK_CPP_STUB4(CallStaticVoidMethod, jni, targetAppClass, agentLoadedMethod, agentNameString);
 230 
 231     return NSK_TRUE;
 232 }
 233 
 234 // this function is used to notify target application that native agent has been finished execution
 235 int nsk_aod_agentFinished(JNIEnv* jni, const char* agentName, int success) {
 236     jstring agentNameString;
 237 
 238     if (!targetAppClass) {
 239         NSK_COMPLAIN1("%s: TEST LOGIC ERROR: method 'agentFinished' was called before "\
 240                 "targetAppClass was initialized\n", agentName);
 241         return NSK_FALSE;
 242     }
 243 
 244     if (internalError && success) {
 245         success = 0;
 246         NSK_COMPLAIN1("Status of agent '%s' is 'passed', but some error happened during test execution "\
 247                 "(see log for details), change agent status to 'failed'\n",
 248                 agentName);
 249     }
 250 
 251     NSK_DISPLAY2("Agent %s finished (success: %d)\n", agentName, success);
 252 
 253     if (agentFinishedMethod == NULL) {
 254         if (!NSK_JNI_VERIFY(jni, (agentFinishedMethod =
 255             NSK_CPP_STUB4(GetStaticMethodID, jni, targetAppClass,
 256                     AGENT_FINISHED_METHOD_NAME, AGENT_FINISHED_METHOD_SIGNATURE)) != NULL))
 257             return NSK_FALSE;
 258     }
 259 
 260     if (!NSK_JNI_VERIFY(jni, (agentNameString = NSK_CPP_STUB2(NewStringUTF, jni, agentName)) != NULL))
 261         return NSK_FALSE;
 262 
 263     NSK_CPP_STUB5(CallStaticVoidMethod, jni, targetAppClass,
 264             agentFinishedMethod, agentNameString, success ? JNI_TRUE : JNI_FALSE);
 265 
 266     return NSK_TRUE;
 267 }
 268 
 269 /*
 270  * Auxiliary functions
 271  */
 272 
 273 // JNI env creation
 274 
 275 JNIEnv* nsk_aod_createJNIEnv(JavaVM* vm) {
 276     JNIEnv* jni;
 277     NSK_CPP_STUB3(GetEnv, vm, (void**)&jni, JNI_VERSION_1_2);
 278 
 279     NSK_VERIFY(jni != NULL);
 280 
 281     return jni;
 282 }
 283 
 284 }