├── .cproject
├── .gitignore
├── Agent.cpp
├── Agent.hpp
├── Agent.png
├── README.md
├── javajam.jar
└── run.sh
/.cproject:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
48 |
49 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Debug/
2 | .project
3 | .settings
4 | .git
5 | .DS_Store
6 |
7 |
--------------------------------------------------------------------------------
/Agent.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Agent.cpp
3 | *
4 | * Created on: Jan 26, 2018
5 | * Author: shaharshocron
6 | */
7 |
8 | //
9 | // Created by Rodrigo Agustin Peinado on 5/7/16.
10 | // Copyright © 2016 Rodrigo Agustin Peinado. All rights reserved.
11 | //
12 | #include "Agent.hpp"
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include "stdlib.h"
18 |
19 | using namespace std;
20 |
21 | #define countof(array) (sizeof(array)/sizeof(array[0]))
22 |
23 | using namespace std;
24 |
25 | bool setsecuritymanager_checked=false;
26 |
27 | // ------
28 | static const char* DUMP_DIRECTORY = "/tmp/";
29 |
30 | //static jrawMonitorID* mutexFlag;
31 |
32 | int main() { return 0; }
33 |
34 | static void check(jvmtiEnv *jvmti, jvmtiError errnum, const char *str)
35 | {
36 | if ( errnum != JVMTI_ERROR_NONE ) {
37 | char *errnum_str = NULL;
38 | jvmti->GetErrorName(errnum, &errnum_str);
39 |
40 | cout << "ERROR: JVMTI";
41 | }
42 | }
43 |
44 | /* -------------------------------------------------------------------
45 |
46 | static void enter_critical_section(jvmtiEnv *jvmti)
47 | {
48 | jvmtiError error = jvmti->RawMonitorEnter(mutex);
49 | check(jvmti, error, "Cannot enter with raw monitor");
50 | }
51 |
52 |
53 | static void exit_critical_section(jvmtiEnv *jvmti)
54 | {
55 | jvmtiError error = jvmti->RawMonitorExit(mutex);
56 | check(jvmti, error, "Cannot exit with raw monitor");
57 | }
58 | --------------------------------------------------------------------*/
59 |
60 | static void printObject(jvmtiEnv *jvmti_env, jthread thread, jvmtiLocalVariableEntry* entry) {
61 | jvmtiError error;
62 | jint int_value_ptr;
63 | jlong long_value_ptr;
64 | jfloat float_value_ptr;
65 | jdouble double_value_ptr;
66 | jobject object_value_ptr;
67 | jint depht = 0;
68 |
69 | //cout << entry->signature << ":" << entry->name << endl;
70 |
71 | switch (entry->signature[0]) {
72 | case 'Z' :
73 | case 'B' :
74 | case 'S' :
75 | case 'C' :
76 | case 'I' :
77 | error = jvmti_env->GetLocalInt(thread, depht, entry->slot, &int_value_ptr);
78 | if(error == JVMTI_ERROR_NONE) {
79 | cout << "Type: Integer, Name: " << entry->name << ", Value: " << int_value_ptr << endl;
80 | } else {
81 | cout << "error: " << error << endl;
82 | }
83 | break;
84 | case 'J' :
85 | error = jvmti_env->GetLocalLong(thread, depht, entry->slot, &long_value_ptr);
86 | if(error == JVMTI_ERROR_NONE) {
87 | cout << "Type: Long, Name: " << entry->name << ", Value: " << long_value_ptr << endl;
88 | } else {
89 | cout << "error: " << error << endl;
90 | }
91 | break;
92 | case 'F' :
93 | error = jvmti_env->GetLocalFloat(thread, depht, entry->slot, &float_value_ptr);
94 | if(error == JVMTI_ERROR_NONE) {
95 | cout << "Type: Float, Name: " << entry->name << ", Value: " << float_value_ptr << endl;
96 | } else {
97 | cout << "error: " << error << endl;
98 | }
99 | break;
100 | case 'D' :
101 | error = jvmti_env->GetLocalDouble(thread, depht, entry->slot, &double_value_ptr);
102 | if(error == JVMTI_ERROR_NONE) {
103 | cout << "Type: Double, Name: " << entry->name << ", Value: " << double_value_ptr << endl;
104 | } else {
105 | cout << "error: " << error << endl;
106 | }
107 | break;
108 | case 'L' :
109 | error = jvmti_env->GetLocalObject(thread, depht, entry->slot, &object_value_ptr);
110 | if(error == JVMTI_ERROR_NONE) {
111 | // get object attributes
112 |
113 | cout << "Type: LocalObject, Name: " << entry->name << ", Length: " << entry->length
114 | << ", Value: " << object_value_ptr << endl;
115 |
116 |
117 | } else {
118 | cout << "error: " << error << endl;
119 | }
120 | break;
121 | }
122 | }
123 |
124 | static const char* getReferenceType(jint type) {
125 | switch (type) {
126 | case 0:
127 | return "Invalid Reference";
128 | case 1:
129 | return "Local Reference";
130 | case 2:
131 | return "Global Reference";
132 | case 3:
133 | return "Weak Global reference";
134 | default:
135 | return "Undefined:";
136 | }
137 | }
138 |
139 | static int getLineNumberForMethod(jvmtiEnv *jvmti_env, jvmtiFrameInfo methodFrame)
140 | {
141 | jvmtiError error;
142 | jvmtiLineNumberEntry* table = NULL;
143 | jint entry_count = 0;
144 | int lineNumberCount;
145 | int lineNumber = -1;
146 | jlocation prevLocationId = -1;
147 | if (methodFrame.location == -1)
148 | return lineNumber;
149 | error = jvmti_env->GetLineNumberTable(methodFrame.method,
150 | &entry_count, &table);
151 |
152 | check(jvmti_env, error, "GetLineNumberTable: ");
153 | if (error == JVMTI_ERROR_NONE)
154 | {
155 | prevLocationId = table[0].start_location;
156 | for (lineNumberCount = 1; lineNumberCount < entry_count; lineNumberCount++)
157 | {
158 | if ((table[lineNumberCount].start_location > methodFrame.location)
159 | && (methodFrame.location >= prevLocationId))
160 | {
161 | lineNumber = table[lineNumberCount - 1].line_number;
162 | break;
163 | }
164 | prevLocationId = table[lineNumberCount].start_location;
165 | }
166 | if (lineNumberCount == entry_count)
167 | {
168 | if (entry_count == 1)
169 | lineNumber = table[lineNumberCount - 1].line_number;
170 | else
171 | lineNumber = -1;
172 | }
173 | }
174 | return lineNumber;
175 | }
176 |
177 | static char * getExceptionMessage(JNIEnv* env, jobject exception)
178 | {
179 | jmethodID getMessageMethodId;
180 | jstring jExceptionMessage;
181 | int msgLength;
182 | char * msgArray = NULL;
183 |
184 | jclass clazz = env->FindClass("java/lang/Throwable");
185 | getMessageMethodId = env->GetMethodID(clazz, "getMessage", "()Ljava/lang/String;");
186 | if (getMessageMethodId != NULL)
187 | {
188 | jExceptionMessage = (jstring) env->CallObjectMethod(exception, getMessageMethodId);
189 | if (jExceptionMessage != NULL)
190 | {
191 | msgLength = env->GetStringLength(jExceptionMessage);
192 | if (msgLength > 0)
193 | {
194 | msgArray = (char *) malloc(msgLength + 1); // To be freed explicitly by the caller of this function
195 | if (msgArray == NULL)
196 | return NULL;
197 | env->GetStringUTFRegion(jExceptionMessage, 0, msgLength, msgArray);
198 | msgArray[msgLength] = '\0';
199 | }
200 | }
201 | }
202 | return msgArray;
203 | }
204 |
205 | static void JNICALL Exception(jvmtiEnv *jvmti_env,JNIEnv* jni_env,jthread thread,jmethodID method,jlocation location,jobject exception,jmethodID catch_method,jlocation catch_location)
206 | {
207 | jint type;
208 | jint entryCount;
209 | jvmtiError error;
210 | jvmtiLocalVariableEntry* localVariableEntry;
211 | localVariableEntry = NULL;
212 | char * exceptionMessage = NULL;
213 |
214 | /*klass=jni_env->GetObjectClass(exception);
215 | jvmti_env->GetMethodName(method,&method_name,&method_signature,&generic_ptr_method);
216 |
217 | cout << "Method: " << method_name << ", Signature: " << method_signature << endl;*/
218 |
219 | type= jni_env->GetObjectRefType(exception);
220 |
221 | cout << "Exception Reference Type: " << getReferenceType(type) << endl;
222 |
223 | if(type>0)
224 | {
225 | exceptionMessage = getExceptionMessage(jni_env, exception);
226 | cout << "Exception Message: " << exceptionMessage << endl;
227 |
228 | //----
229 | jint num_frames=0;
230 | error = jvmti_env->GetFrameCount(thread, &num_frames);
231 | if (error != JVMTI_ERROR_NONE) {
232 | cout << "Could not get the frame count." << endl;
233 | return;
234 | }
235 |
236 | jvmtiFrameInfo frames[num_frames];
237 | if(num_frames > 0)
238 | {
239 | jint count;
240 | error = jvmti_env->GetStackTrace(NULL, 0, 10, (jvmtiFrameInfo*)&frames, &count);
241 | cout << "Stacktrace: Frame count=" << count << " num of frames: " << num_frames << endl << "------------------------------------------------" << endl;
242 |
243 | if (error != JVMTI_ERROR_NONE) {
244 | printf("(GetThreadInfo) Error expected: %d, got: %d\n", JVMTI_ERROR_NONE, error);
245 | //describe(err);
246 | printf("\n");
247 | }
248 | else if (error == JVMTI_ERROR_NONE && count >=1) {
249 | printf("Number of records filled: %d\n", count);
250 | char *methodName;
251 | //methodName = "yet_to_call()";
252 | char *declaringClassName;
253 | jclass declaring_class;
254 |
255 | printf("Exception Stack Trace\n");
256 | printf("=====================\n");
257 | printf("Stack Trace Depth: %d\n", count);
258 |
259 | for (int i=0; i < count; i++) {
260 | error = jvmti_env->GetMethodName(frames[i].method, &methodName, NULL, NULL);
261 | if (error == JVMTI_ERROR_NONE) {
262 | error = jvmti_env->GetMethodDeclaringClass(frames[i].method, &declaring_class);
263 | error = jvmti_env->GetClassSignature(declaring_class, &declaringClassName, NULL);
264 | int lineNum = getLineNumberForMethod(jvmti_env, frames[i]);
265 | if (error == JVMTI_ERROR_NONE) {
266 | printf("at method %s( %d ) in class %s\n", methodName, lineNum, declaringClassName);
267 | }
268 | }
269 |
270 | error = jvmti_env->GetLocalVariableTable(frames[0].method, &entryCount, &localVariableEntry);
271 |
272 | if(error == JVMTI_ERROR_NONE) {
273 |
274 | jvmtiLocalVariableEntry* entry = localVariableEntry;
275 | cout << "Variables: " << entryCount << endl;
276 |
277 | for(int i = 0; i < entryCount; i++, entry++) {
278 | // print object's details
279 | printObject(jvmti_env, thread, entry);
280 |
281 | jvmti_env->Deallocate(reinterpret_cast(entry->signature));
282 | jvmti_env->Deallocate(reinterpret_cast(entry->name));
283 | jvmti_env->Deallocate(reinterpret_cast(entry->generic_signature));
284 | }
285 | jvmti_env->Deallocate(reinterpret_cast(localVariableEntry));
286 | } else if(error == JVMTI_ERROR_ABSENT_INFORMATION) {
287 | cout << "" << endl;
288 | } else {
289 | cout << "" << endl;
290 | }
291 | }
292 | }
293 | }
294 | }
295 | if(exceptionMessage != NULL)
296 | free(exceptionMessage);
297 | }
298 |
299 | //-------
300 | static void JNICALL loadClass(jvmtiEnv *jvmti_env,JNIEnv* jni_env,jthread thread,jclass klass)
301 | {
302 | char *class_name;
303 | char *generic_ptr_class;
304 |
305 | jvmti_env->GetClassSignature(klass, &class_name,&generic_ptr_class);
306 |
307 | cout << "loadClass: Agent_Registered" << endl;
308 | jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION,(jthread)NULL);
309 | jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,JVMTI_EVENT_CLASS_PREPARE,(jthread)NULL);
310 |
311 | jvmti_env->Deallocate((unsigned char *)class_name);
312 | jvmti_env->Deallocate((unsigned char *)generic_ptr_class);
313 | }
314 |
315 | static bool startsWith(const char* str, const char* subStr) {
316 | // look for subStr at the start of str
317 | if ( strncmp(subStr, str, strlen(subStr)) == 0 )
318 | return true;
319 | return false;
320 | }
321 |
322 | const char* clazzes[] = { "java", "sun", "scala" };
323 |
324 | static bool isUserClass(const char* clz) {
325 | for(int i=0; i<3; i++)
326 | if( startsWith(clz, clazzes[i]) )
327 | return false;
328 | return true;
329 | }
330 |
331 | static void JNICALL showClass(jvmtiEnv *jvmti,
332 | JNIEnv* env,
333 | jclass class_being_redefined,
334 | jobject loader,
335 | const char* name,
336 | jobject protection_domain,
337 | jint class_data_len,
338 | const unsigned char* class_data,
339 | jint* new_class_data_len,
340 | unsigned char** new_class_data)
341 | {
342 | //enter_critical_section(jvmti); {
343 | if(isUserClass(name))
344 | {
345 | cout << "Loading Class: " << name << "\n";
346 | std::string str(name);
347 | for(int i=0; iGetEnv((void **)&jvmti, JVMTI_VERSION_1);
378 | if (res != JNI_OK||jvmti==NULL)
379 | {
380 | cout << "ERROR: Unable to access JVMTI Version 1" << endl;
381 | }
382 |
383 |
384 | (void)memset(&capabilities,0, sizeof(capabilities));
385 |
386 | capabilities.can_generate_exception_events=1;
387 | capabilities.can_get_line_numbers=1;
388 | capabilities.can_access_local_variables=1;
389 | capabilities.can_tag_objects=1;
390 | //---
391 | capabilities.can_generate_all_class_hook_events = 1;
392 |
393 | jvmtiError error = jvmti->AddCapabilities(&capabilities);
394 |
395 | check(jvmti,error,"Unable to get necessary capabilities.");
396 |
397 | (void)memset(&callbacks,0, sizeof(callbacks));
398 |
399 | //---
400 | //callbacks.ClassPrepare = &loadClass;
401 | callbacks.ClassFileLoadHook = &showClass;
402 | callbacks.Exception=&Exception;
403 |
404 | jvmti->SetEventCallbacks(&callbacks, (jint)sizeof(callbacks));
405 |
406 | //---
407 | jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL);
408 | //jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_CLASS_PREPARE,(jthread)NULL);
409 |
410 | jvmti->SetEventNotificationMode(JVMTI_ENABLE,JVMTI_EVENT_EXCEPTION,(jthread)NULL);
411 |
412 | //jvmti->CreateRawMonitor("agent data", &mutex);
413 | // check(jvmti, error, "Cannot create raw monitor");
414 |
415 | return JNI_OK;
416 | }
417 |
418 |
419 | JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
420 | {
421 | cout << "Agent_OnUnload" << endl;
422 | }
423 |
424 |
425 |
--------------------------------------------------------------------------------
/Agent.hpp:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // tipiki.hpp
4 | // tipiki
5 | //
6 | // Created by Rodrigo Agustin Peinado on 5/7/16.
7 | // Copyright © 2016 Rodrigo Agustin Peinado. All rights reserved.
8 | //
9 |
10 | #ifndef agent_hpp
11 | #define agent_hpp
12 |
13 | #include
14 |
15 | #endif /* agent_hpp */
16 |
--------------------------------------------------------------------------------
/Agent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LionHeart55/JavaAgent/3a71f94c2a96dbe5cbb721d1792329af38bf8e04/Agent.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Agent
2 |
3 | ## Introduction
4 | This c++ project is an example of how to implement JVMTI library to attach to a JVM and gather data on exception in runtime.
5 |
6 | 1) tap into the load classes capabilities, get filename, read it ans save class file to /tmp/
7 | 2) tap into exception capabilities
8 | - Get Exception message
9 | - Get Exception class
10 | - Get stack frames and for each frame
11 | - Get Class.Method.
12 | - Get line Number.
13 | - Get Variable names and values.
14 |
15 | ## Prerequisites
16 |
17 | Need to include the JVMTI header library located under:
18 | goto project-->properties choose C/C++ General--> Paths and symbols, then choose includes ==> GNU C++
19 | then add these directories within your JDK folder for example:
20 | /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include
21 | /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/include/darwin
22 |
23 | To build in eclipse choose project right click and click 'Build All'
24 | if this does not work you will need to create an new project in your IDE and import the sources.
25 |
26 | ## Usage
27 |
28 | java -agentpath:Debug/Agent -jar javajam.jar
29 |
30 | OR
31 |
32 | sudo chmod +x run.sh
33 |
34 | ./run.sh
35 |
36 | 
37 |
38 |
39 | ## Sample output
40 |
41 | Exception Reference Type: Local Reference
42 |
43 | Exception Message: Throwing a test exception: IllegalThreadStateException
44 |
45 | Stacktrace: Frame count=2 num of frames: 2
46 |
47 | Number of records filled: 2
48 |
49 | Exception Stack Trace
50 |
51 | Stack Trace Depth: 2
52 |
53 | at method Throwing( 47 ) in class LMain/Main;
54 |
55 | Variables: 1
56 |
57 | Type: Integer, Name: value, Value: 8
58 |
59 | at method main( 19 ) in class LMain/Main;
60 |
61 | Variables: 1
62 |
63 | Type: Integer, Name: value, Value: 8
64 |
--------------------------------------------------------------------------------
/javajam.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LionHeart55/JavaAgent/3a71f94c2a96dbe5cbb721d1792329af38bf8e04/javajam.jar
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | java -agentpath:Debug/Agent -jar javajam.jar
2 |
--------------------------------------------------------------------------------