#include
#include
#include
#include
#include "agent.h"
#include "cpu_profiler.h"
#include "lock_profiler.h"
#include
#include
using namespace std;
Agent Agent::instance;
void Agent::LogMessage(string level, string msg) {
if (debug_mode) {
cout << "StackImpact Agent: " << level << ": " << msg << endl;
}
}
void Agent::LogInfo(string msg) {
LogMessage("INFO", msg);
}
void Agent::LogError(string msg) {
LogMessage("ERROR", msg);
}
bool Agent::CheckJVMTIError(jvmtiEnv *jvmti, jvmtiError error, string msg) {
if (error != JVMTI_ERROR_NONE) {
if (debug_mode) {
char *error_str;
error_str = NULL;
jvmti->GetErrorName(error, &error_str);
ostringstream error_message;
error_message << "JVMTI: "
<< (error_str == NULL ? "Unknown" : error_str)
<< " (" << error << "): "
<< msg;
LogError(error_message.str());
return false;
}
}
return true;
}
void Agent::Init(JavaVM* vm) {
Agent* agent = &Agent::instance;
jvm = vm;
int rc = jvm->GetEnv((void**)&agent->jni, JNI_VERSION_1_6);
if (rc != JNI_OK && rc != JNI_EDETACHED) {
agent->LogError("GetEnv, JNI_VERSION_1_6");
return;
}
rc = jvm->GetEnv((void **)&agent->jvmti, JVMTI_VERSION_1_2);
if (rc != JNI_OK) {
agent->LogError("GetEnv, JVMTI_VERSION_1_2");
return;
}
}
void LoadMethodIDs(jvmtiEnv* jvmti, jclass klass) {
jint num_methods;
jmethodID* methods;
if (jvmti->GetClassMethods(klass, &num_methods, &methods) == 0) {
jvmti->Deallocate((unsigned char*)methods);
}
}
void LoadAllMethodIDs(jvmtiEnv* jvmti) {
jint num_classes;
jclass* classes;
if (jvmti->GetLoadedClasses(&num_classes, &classes) == 0) {
for (int i = 0; i < num_classes; i++) {
LoadMethodIDs(jvmti, classes[i]);
}
jvmti->Deallocate((unsigned char*)classes);
}
}
void JNICALL ClassLoad(jvmtiEnv *jvmti_env, JNIEnv *jni_env, jthread thread,
jclass klass) {
}
void JNICALL ClassPrepare(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread,
jclass klass) {
LoadMethodIDs(jvmti_env, klass);
}
void JNICALL CompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method,
jint code_size, const void* code_addr,
jint map_length, const jvmtiAddrLocationMap* map,
const void* compile_info) {
}
bool Agent::Attach() {
if (is_attached) {
return true;
}
cpu_profiler = new CPUProfiler();
lock_profiler = new LockProfiler();
if (dlsym(RTLD_DEFAULT, "AsyncGetCallTrace") == NULL) {
LogError("AsyncGetCallTrace not available.");
return false;
}
jvmtiError error;
jvmtiCapabilities potentialCapabilities = {0};
jvmti->GetPotentialCapabilities(&potentialCapabilities);
jvmtiCapabilities capabilities = {0};
jvmtiEventCallbacks callbacks = {0};
callbacks.ClassLoad = ClassLoad;
callbacks.ClassPrepare = ClassPrepare;
if (potentialCapabilities.can_generate_compiled_method_load_events) {
capabilities.can_generate_compiled_method_load_events = 1;
callbacks.CompiledMethodLoad = CompiledMethodLoad;
}
else {
LogInfo("Missing capability: can_generate_compiled_method_load_events");
}
if (potentialCapabilities.can_get_source_file_name) {
capabilities.can_get_source_file_name = 1;
}
else {
LogInfo("Missing capability: can_get_source_file_name");
}
if (potentialCapabilities.can_get_line_numbers) {
capabilities.can_get_line_numbers = 1;
}
else {
LogInfo("Missing capability: can_get_line_numbers");
}
if (potentialCapabilities.can_generate_monitor_events) {
capabilities.can_generate_monitor_events = 1;
callbacks.MonitorContendedEnter = LockProfiler::MonitorContendedEnter;
callbacks.MonitorContendedEntered = LockProfiler::MonitorContendedEntered;
}
else {
LogInfo("Missing capability: can_generate_monitor_events");
}
if (potentialCapabilities.can_tag_objects) {
capabilities.can_tag_objects = 1;
}
else {
LogInfo("Missing capability: can_tag_objects");
}
error = jvmti->AddCapabilities(&capabilities);
CheckJVMTIError(jvmti, error, "AddCapabilities");
error = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
CheckJVMTIError(jvmti, error, "SetEventCallbacks");
jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL);
jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
//error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, NULL);
CheckJVMTIError(jvmti, error, "SetEventNotificationMode");
// Initialize methodIDs by calling GetClassMethods, needed for AsyncGetCallTrace to function.
LoadAllMethodIDs(jvmti);
jvmti->GenerateEvents(JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
jvmti->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD);
is_attached = true;
LogInfo("The JVMTI agent has been attached successfuly.");
return true;
}
extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
// the agent is not loaded via agentpath, it's loaded from java agent
return JNI_ERR;
}
extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
return JNI_ERR;
}
extern "C" JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) {
Agent::instance.LogInfo("Agent_OnUnload");
}
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* jvm, void* reserved) {
Agent* agent = &Agent::instance;
agent->Init(jvm);
return JNI_VERSION_1_6;
}
extern "C" JNIEXPORT jboolean JNICALL Java_com_stackimpact_agent_Agent_attach(JNIEnv* env, jobject unused, jboolean debug_mode) {
Agent* agent = &Agent::instance;
agent->jni = env;
agent->debug_mode = (bool)debug_mode;
return agent->Attach();
}