text: /*
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You under the Apache License, Version 2.0
   *  (the "License"); you may not use this file except in compliance with
   *  the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  
  /**
   * @author Pavel Rebriy
   */
  
  #include "agent.h"
  
  #define CHECK_CAP(obtained, needed, jvmti_cap)              \
      if(!obtained.jvmti_cap) {                               \
      REPORT( "Cannot set capability: "#jvmti_cap );          \
          return JNI_ERR;                                     \
      }                                                       \
      needed.jvmti_cap = 1;
  
  void JNICALL 
  agent_callback_MethodEntry( jvmtiEnv* jvmti_env,
                              JNIEnv* jni_env,
                              jthread thread,
                              jmethodID method);
  
  void JNICALL
  agent_callback_Breakpoint( jvmtiEnv* jvmti_env,
                             JNIEnv* jni_env,
                             jthread thread,
                             jmethodID method,
                             jlocation location);
  
  void JNICALL 
  agent_callback_SingleStep( jvmtiEnv * jvmti_env,
                             JNIEnv * jni_env,
                             jthread thread,
                             jmethodID method,
                             jlocation location);
  
  static inline jint
  set_agent_capabilities(jvmtiEnv * jvmti_env)
  {
      // get VM capabilities
      jvmtiCapabilities vm_cap;
      jvmtiError result = jvmti_env->GetPotentialCapabilities(&vm_cap);
      if (result != JVMTI_ERROR_NONE) {
          REPORT( "get potential capabilities" );
          return JNI_ERR;
      }
  
      jvmtiCapabilities need_cap = {0};
      CHECK_CAP(vm_cap, need_cap, can_generate_breakpoint_events);
      CHECK_CAP(vm_cap, need_cap, can_generate_method_entry_events);
      CHECK_CAP(vm_cap, need_cap, can_get_line_numbers);
      CHECK_CAP(vm_cap, need_cap, can_generate_single_step_events);
  
      result = jvmti_env->AddCapabilities(&need_cap);
      if (result != JVMTI_ERROR_NONE) {
          REPORT( "set needed capabilities" );
          return JNI_ERR;
      }
      return JNI_OK;
  }
  
  static inline jint
  set_agent_events(jvmtiEnv * jvmti_env)
  {
      jvmtiEventCallbacks callbacks;
      memset(&callbacks, 0, sizeof(callbacks));
  
      // set event callbacks
      callbacks.MethodEntry = &agent_callback_MethodEntry;
      callbacks.Breakpoint = &agent_callback_Breakpoint;
      callbacks.SingleStep = &agent_callback_SingleStep;
      jvmtiError result = jvmti_env->SetEventCallbacks(&callbacks, (jint)sizeof(callbacks));
      if( result != JVMTI_ERROR_NONE ) {
          REPORT("set events callbacks");
          return JNI_ERR;
      }
  
      // set MethodEntry event
      result = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, NULL);
      if( result != JVMTI_ERROR_NONE ) {
          REPORT("enable MethodEntry event");
          return JNI_ERR;
      }
  
      return JNI_OK;
  }
  
  JNIEXPORT jint JNICALL
  Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
  {
      // get JVMTI enviroment
      jvmtiEnv * jvmti_env;
      DEBUG("Getting JVMTI enviroment... ");
      if( vm->GetEnv( (void**)&jvmti_env, JVMTI_VERSION_1_0) != JNI_OK ) {
          REPORT( "get JVMTI enviroment" );
          return JNI_OK;
      }
      DEBUG("done!");
      
      // set capabilities
      DEBUG("Setting capabilities... ");
      if( set_agent_capabilities(jvmti_env) != JNI_OK ) {
          return JNI_OK;
      }
      DEBUG("done!");
  
      // set events
      DEBUG("Setting events... ");
      if( set_agent_events(jvmti_env) != JNI_OK ) {
          return JNI_OK;
      }
      DEBUG("done!");
  
      return JNI_OK;
  }
  