Skip to content

Commit 7c9e428

Browse files
committed
Prototype JIT debug agent
1 parent c022c27 commit 7c9e428

File tree

12 files changed

+520
-2
lines changed

12 files changed

+520
-2
lines changed

jcl/src/java.base/share/classes/com/ibm/jit/JITHelpers.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,18 @@
2727
import com.ibm.oti.vm.J9UnmodifiableClass;
2828
import java.lang.reflect.Field;
2929
import java.lang.reflect.Array;
30+
import java.lang.reflect.InvocationTargetException;
3031
import com.ibm.oti.vm.VM;
3132
/*[IF Sidecar19-SE]
3233
import jdk.internal.misc.Unsafe;
3334
import jdk.internal.reflect.Reflection;
3435
import jdk.internal.reflect.CallerSensitive;
36+
import jdk.internal.reflect.MethodAccessor;
3537
/*[ELSE]*/
3638
import sun.misc.Unsafe;
37-
import sun.reflect.Reflection;
3839
import sun.reflect.CallerSensitive;
40+
import sun.reflect.MethodAccessor;
41+
import sun.reflect.Reflection;
3942
/*[ENDIF]*/
4043

4144
/**
@@ -1174,4 +1177,24 @@ public int getInitialLockword(int flags, int reservedCounter, int cancelCounter)
11741177
public static native void dispatchComputedStaticCall();
11751178

11761179
public static native void dispatchVirtual();
1180+
1181+
private native static final void debugAgentRun(MethodAccessor ma, Object obj, Object[] args);
1182+
1183+
public static Object invoke(MethodAccessor ma, Object obj, Object[] args) throws InvocationTargetException {
1184+
try {
1185+
return ma.invoke(obj, args);
1186+
} catch (InvocationTargetException e) {
1187+
if (e.getCause() != null && e.getCause().getClass().getName().equals("java.lang.AssertionError")) {
1188+
// TODO: Need synchronization to prevent many threads entering here
1189+
System.err.println("Caught org.opentest4j.AssertionFailedError inside JITHelpers");
1190+
1191+
debugAgentRun(ma, obj, args);
1192+
1193+
System.err.println("Aborting JVM");
1194+
System.exit(1);
1195+
}
1196+
1197+
throw e;
1198+
}
1199+
}
11771200
}

runtime/compiler/build/files/common.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ JIT_PRODUCT_SOURCE_FILES+=\
280280
compiler/compile/J9SymbolReferenceTable.cpp \
281281
compiler/control/CompilationController.cpp \
282282
compiler/control/CompilationThread.cpp \
283+
compiler/control/DebugAgent.cpp \
283284
compiler/control/DLLMain.cpp \
284285
compiler/control/HookedByTheJit.cpp \
285286
compiler/control/J9Options.cpp \

runtime/compiler/control/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
j9jit_files(
2424
control/CompilationController.cpp
2525
control/CompilationThread.cpp
26+
control/DebugAgent.cpp
2627
control/DLLMain.cpp
2728
control/HookedByTheJit.cpp
2829
control/J9Options.cpp
Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2021, 2021 IBM Corp. and others
3+
*
4+
* This program and the accompanying materials are made available under
5+
* the terms of the Eclipse Public License 2.0 which accompanies this
6+
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7+
* or the Apache License, Version 2.0 which accompanies this distribution and
8+
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9+
*
10+
* This Source Code may also be made available under the following
11+
* Secondary Licenses when the conditions for such availability set
12+
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13+
* General Public License, version 2 with the GNU Classpath
14+
* Exception [1] and GNU General Public License, version 2 with the
15+
* OpenJDK Assembly Exception [2].
16+
*
17+
* [1] https://www.gnu.org/software/classpath/license.html
18+
* [2] http://openjdk.java.net/legal/assembly-exception.html
19+
*
20+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21+
*******************************************************************************/
22+
23+
#include "control/DebugAgent.hpp"
24+
25+
#include "codegen/CodeGenerator.hpp"
26+
#include "control/MethodToBeCompiled.hpp"
27+
#include "control/CompilationRuntime.hpp"
28+
#include "control/CompilationThread.hpp"
29+
#include "env/ut_j9jit.h"
30+
#include "env/VMAccessCriticalSection.hpp"
31+
#include "env/VMJ9.h"
32+
#include "ilgen/J9ByteCodeIlGenerator.hpp"
33+
#include "jithash.h"
34+
#include "nls/j9dmpnls.h"
35+
#include <queue>
36+
#include <set>
37+
38+
BOOLEAN
39+
debugAgentStart(J9VMThread* vmThread)
40+
{
41+
PORT_ACCESS_FROM_VMC(vmThread);
42+
43+
J9JITConfig *jitConfig = vmThread->javaVM->jitConfig;
44+
if (NULL == jitConfig)
45+
{
46+
fprintf(stderr, "Could not locate J9JITConfig\n");
47+
return false;
48+
}
49+
50+
TR::CompilationInfo *compInfo = TR::CompilationInfo::get(jitConfig);
51+
if (NULL == compInfo)
52+
{
53+
fprintf(stderr, "Could not locate TR::CompilationInfo\n");
54+
return false;
55+
}
56+
57+
TR_J9VMBase *frontendOfThread = TR_J9VMBase::get(jitConfig, vmThread);
58+
if (NULL == frontendOfThread)
59+
{
60+
fprintf(stderr, "Could not locate TR_J9VMBase\n");
61+
return false;
62+
}
63+
64+
// To avoid a deadlock, release compilation monitor until we are no longer holding it
65+
while (compInfo->getCompilationMonitor()->owned_by_self())
66+
{
67+
compInfo->releaseCompMonitor(vmThread);
68+
}
69+
70+
// Release other monitors as well. In particular CHTable and classUnloadMonitor must not be held.
71+
while (TR::MonitorTable::get()->getClassTableMutex()->owned_by_self())
72+
{
73+
frontendOfThread->releaseClassTableMutex(false);
74+
}
75+
76+
TR::CompilationInfoPerThread *threadCompInfo = compInfo->getCompInfoForThread(vmThread);
77+
if (NULL != threadCompInfo)
78+
{
79+
TR_MethodToBeCompiled *methodBeingCompiled = threadCompInfo->getMethodBeingCompiled();
80+
81+
// If we are currently compiling a method, wake everyone waiting for it to compile
82+
if (NULL != methodBeingCompiled && NULL != methodBeingCompiled->getMonitor())
83+
{
84+
methodBeingCompiled->getMonitor()->enter();
85+
methodBeingCompiled->getMonitor()->notifyAll();
86+
methodBeingCompiled->getMonitor()->exit();
87+
88+
fprintf(stderr, "Notified threads waiting\n");
89+
}
90+
}
91+
92+
compInfo->getPersistentInfo()->setDisableFurtherCompilation(true);
93+
94+
TR::CompilationInfoPerThread *recompilationThreadInfo = compInfo->getCompilationInfoForDiagnosticThread();
95+
if (NULL == recompilationThreadInfo)
96+
{
97+
j9nls_printf(PORTLIB, J9NLS_ERROR | J9NLS_STDERR, J9NLS_DMP_ERROR_IN_DUMP_STR, "JIT", "Could not locate the diagnostic thread info");
98+
return OMR_ERROR_INTERNAL;
99+
}
100+
101+
auto *recompilationThread = recompilationThreadInfo->getCompilationThread();
102+
if (NULL == recompilationThread)
103+
{
104+
j9nls_printf(PORTLIB, J9NLS_ERROR | J9NLS_STDERR, J9NLS_DMP_ERROR_IN_DUMP_STR, "JIT", "Could not locate the diagnostic thread");
105+
return OMR_ERROR_INTERNAL;
106+
}
107+
108+
compInfo->acquireCompMonitor(vmThread);
109+
compInfo->purgeMethodQueue(compilationFailure);
110+
compInfo->releaseCompMonitor(vmThread);
111+
112+
recompilationThreadInfo->resumeCompilationThread();
113+
114+
return true;
115+
}
116+
117+
BOOLEAN
118+
debugAgentGetAllJitMethods(J9VMThread* vmThread, jobject jitMethodSet)
119+
{
120+
JNIEnv *env = (JNIEnv*)vmThread;
121+
122+
jclass java_lang_Long = env->FindClass("java/lang/Long");
123+
jmethodID java_lang_Long_init = env->GetMethodID(java_lang_Long, "<init>", "(J)V");
124+
jmethodID java_lang_Long_longValue = env->GetMethodID(java_lang_Long, "longValue", "()J");
125+
126+
jclass java_util_HashSet = env->FindClass("java/util/HashSet");
127+
jmethodID java_util_HashSet_init = env->GetMethodID(java_util_HashSet, "<init>", "()V");
128+
jmethodID java_util_HashSet_add = env->GetMethodID(java_util_HashSet, "add", "(Ljava/lang/Object;)Z");
129+
jmethodID java_util_HashSet_size = env->GetMethodID(java_util_HashSet, "size", "()I");
130+
131+
jclass java_util_LinkedList = env->FindClass("java/util/LinkedList");
132+
jmethodID java_util_LinkedList_init = env->GetMethodID(java_util_LinkedList, "<init>", "()V");
133+
jmethodID java_util_LinkedList_add = env->GetMethodID(java_util_LinkedList, "add", "(Ljava/lang/Object;)Z");
134+
jmethodID java_util_LinkedList_isEmpty = env->GetMethodID(java_util_LinkedList, "isEmpty", "()Z");
135+
jmethodID java_util_LinkedList_remove = env->GetMethodID(java_util_LinkedList, "remove", "()Ljava/lang/Object;");
136+
137+
jobject jitAVLQueue = env->NewObject(java_util_LinkedList, java_util_LinkedList_init);
138+
139+
jobject rootNode = env->NewObject(java_lang_Long, java_lang_Long_init, (jlong)jitConfig->translationArtifacts->rootNode);
140+
env->CallBooleanMethod(jitAVLQueue, java_util_LinkedList_add, rootNode);
141+
env->DeleteLocalRef(rootNode);
142+
143+
jboolean jitAVLQueueIsEmpty = env->CallBooleanMethod(jitAVLQueue, java_util_LinkedList_isEmpty);
144+
while (jitAVLQueueIsEmpty != JNI_TRUE)
145+
{
146+
jobject nodeObject = env->CallObjectMethod(jitAVLQueue, java_util_LinkedList_remove);
147+
J9AVLTreeNode *node = (J9AVLTreeNode *)env->CallLongMethod(nodeObject, java_lang_Long_longValue);
148+
env->DeleteLocalRef(nodeObject);
149+
150+
if (NULL != node)
151+
{
152+
jobject leftChild = env->NewObject(java_lang_Long, java_lang_Long_init, (jlong)J9AVLTREENODE_LEFTCHILD(node));
153+
jobject rightChild = env->NewObject(java_lang_Long, java_lang_Long_init, (jlong)J9AVLTREENODE_RIGHTCHILD(node));
154+
env->CallBooleanMethod(jitAVLQueue, java_util_LinkedList_add, leftChild);
155+
env->CallBooleanMethod(jitAVLQueue, java_util_LinkedList_add, rightChild);
156+
157+
158+
J9JITHashTableWalkState state;
159+
J9JITExceptionTable* metadata = hash_jit_start_do(&state, reinterpret_cast<J9JITHashTable*>(node));
160+
while (NULL != metadata)
161+
{
162+
jobject jitMethod = env->NewObject(java_lang_Long, java_lang_Long_init, (jlong)metadata);
163+
env->CallBooleanMethod(jitMethodSet, java_util_HashSet_add, jitMethod);
164+
env->DeleteLocalRef(jitMethod);
165+
166+
metadata = hash_jit_next_do(&state);
167+
}
168+
169+
env->DeleteLocalRef(leftChild);
170+
env->DeleteLocalRef(rightChild);
171+
}
172+
173+
jitAVLQueueIsEmpty = env->CallBooleanMethod(jitAVLQueue, java_util_LinkedList_isEmpty);
174+
}
175+
176+
env->DeleteLocalRef(java_lang_Long);
177+
env->DeleteLocalRef(java_util_HashSet);
178+
env->DeleteLocalRef(java_util_LinkedList);
179+
env->DeleteLocalRef(jitAVLQueue);
180+
181+
return true;
182+
}
183+
184+
BOOLEAN
185+
debugAgentRevertToInterpreter(J9VMThread* vmThread, J9JITExceptionTable *jitMethod)
186+
{
187+
J9JITConfig *jitConfig = vmThread->javaVM->jitConfig;
188+
if (NULL == jitConfig)
189+
{
190+
fprintf(stderr, "Could not locate J9JITConfig\n");
191+
return false;
192+
}
193+
194+
TR::CompilationInfo *compInfo = TR::CompilationInfo::get(jitConfig);
195+
if (NULL == compInfo)
196+
{
197+
fprintf(stderr, "Could not locate TR::CompilationInfo\n");
198+
return false;
199+
}
200+
201+
TR_J9VMBase *frontendOfThread = TR_J9VMBase::get(jitConfig, vmThread);
202+
if (NULL == frontendOfThread)
203+
{
204+
fprintf(stderr, "Could not locate TR_J9VMBase\n");
205+
return false;
206+
}
207+
208+
TR_PersistentJittedBodyInfo *bodyInfo = reinterpret_cast<TR_PersistentJittedBodyInfo *>(jitMethod->bodyInfo);
209+
if (NULL == bodyInfo)
210+
{
211+
fprintf(stderr, "Could not locate persistent body info for JIT method %p\n", jitMethod);
212+
return false;
213+
}
214+
215+
PORT_ACCESS_FROM_VMC(vmThread);
216+
J9Class *clazz = J9_CLASS_FROM_METHOD(jitMethod->ramMethod);
217+
J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(jitMethod->ramMethod);
218+
J9UTF8 *methName = J9ROMMETHOD_NAME(romMethod);
219+
J9UTF8 *methSig = J9ROMMETHOD_SIGNATURE(romMethod);
220+
J9UTF8 *className = J9ROMCLASS_CLASSNAME(clazz->romClass);
221+
222+
void *pc = compInfo->getPCIfCompiled(jitMethod->ramMethod);
223+
fprintf(stderr, "Invalidating PC = %p %.*s.%.*s%.*s\n", pc,
224+
(UDATA)J9UTF8_LENGTH(className), J9UTF8_DATA(className),
225+
(UDATA)J9UTF8_LENGTH(methName), J9UTF8_DATA(methName),
226+
(UDATA)J9UTF8_LENGTH(methSig), J9UTF8_DATA(methSig));
227+
228+
TR::Recompilation::methodCannotBeRecompiled(pc, frontendOfThread);
229+
230+
return true;
231+
}
232+
233+
extern J9_CFUNC BOOLEAN
234+
debugAgentRecompile(J9VMThread* vmThread, J9JITExceptionTable *jitMethod, IDATA lastOptIndex, IDATA lastOptSubIndex, BOOLEAN enableTracing)
235+
{
236+
J9JITConfig *jitConfig = vmThread->javaVM->jitConfig;
237+
if (NULL == jitConfig)
238+
{
239+
fprintf(stderr, "Could not locate J9JITConfig\n");
240+
return false;
241+
}
242+
243+
TR::CompilationInfo *compInfo = TR::CompilationInfo::get(jitConfig);
244+
if (NULL == compInfo)
245+
{
246+
fprintf(stderr, "Could not locate TR::CompilationInfo\n");
247+
return false;
248+
}
249+
250+
TR_J9VMBase *frontendOfThread = TR_J9VMBase::get(jitConfig, vmThread);
251+
if (NULL == frontendOfThread)
252+
{
253+
fprintf(stderr, "Could not locate TR_J9VMBase\n");
254+
return false;
255+
}
256+
257+
TR_PersistentJittedBodyInfo *bodyInfo = reinterpret_cast<TR_PersistentJittedBodyInfo *>(jitMethod->bodyInfo);
258+
if (NULL == bodyInfo)
259+
{
260+
fprintf(stderr, "Could not locate persistent body info for JIT method %p\n", jitMethod);
261+
return false;
262+
}
263+
264+
PORT_ACCESS_FROM_VMC(vmThread);
265+
J9Class *clazz = J9_CLASS_FROM_METHOD(jitMethod->ramMethod);
266+
J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(jitMethod->ramMethod);
267+
J9UTF8 *methName = J9ROMMETHOD_NAME(romMethod);
268+
J9UTF8 *methSig = J9ROMMETHOD_SIGNATURE(romMethod);
269+
J9UTF8 *className = J9ROMCLASS_CLASSNAME(clazz->romClass);
270+
271+
void *pc = compInfo->getPCIfCompiled(jitMethod->ramMethod);
272+
fprintf(stderr, "Recompiling PC = %p lastOptIndex = %d lastOptSubIndex = %d %.*s.%.*s%.*s\n", pc, lastOptIndex, lastOptSubIndex,
273+
(UDATA)J9UTF8_LENGTH(className), J9UTF8_DATA(className),
274+
(UDATA)J9UTF8_LENGTH(methName), J9UTF8_DATA(methName),
275+
(UDATA)J9UTF8_LENGTH(methSig), J9UTF8_DATA(methSig));
276+
277+
// The request to use a trace log gets passed to the compilation via the optimization plan. The options object
278+
// created before the compile is issued will use the trace log we provide to initialize IL tracing.
279+
TR_OptimizationPlan *plan = TR_OptimizationPlan::alloc(bodyInfo->getHotness());
280+
if (NULL == plan)
281+
{
282+
j9nls_printf(PORTLIB, J9NLS_INFO | J9NLS_STDERR, J9NLS_DMP_JIT_OPTIMIZATION_PLAN);
283+
return false;
284+
}
285+
286+
plan->setInsertInstrumentation(bodyInfo->getIsProfilingBody());
287+
// plan->setLogCompilation(jitdumpFile);
288+
289+
TR::Options::getCmdLineOptions()->setLastOptIndex(lastOptIndex);
290+
TR::Options::getCmdLineOptions()->setLastOptSubIndex(lastOptSubIndex);
291+
292+
// This API is meant to be called from within JNI so we must acquire VM access here before queuing the compilation
293+
// beacuse we will attempt to release VM access right before a synchronous compilation
294+
vmThread->javaVM->internalVMFunctions->internalAcquireVMAccess(vmThread);
295+
296+
J9::JitDumpMethodDetails details(jitMethod->ramMethod, NULL, bodyInfo->getIsAotedBody());
297+
auto rc = compilationOK;
298+
auto queued = false;
299+
compInfo->compileMethod(vmThread, details, pc, TR_no, &rc, &queued, plan);
300+
301+
vmThread->javaVM->internalVMFunctions->internalReleaseVMAccess(vmThread);
302+
303+
return true;
304+
}
305+
306+
BOOLEAN
307+
debugAgentEnd(J9VMThread* vmThread)
308+
{
309+
J9JITConfig *jitConfig = vmThread->javaVM->jitConfig;
310+
if (NULL == jitConfig)
311+
{
312+
fprintf(stderr, "Could not locate J9JITConfig\n");
313+
return false;
314+
}
315+
316+
TR::CompilationInfo *compInfo = TR::CompilationInfo::get(jitConfig);
317+
if (NULL == compInfo)
318+
{
319+
fprintf(stderr, "Could not locate TR::CompilationInfo\n");
320+
return false;
321+
}
322+
323+
compInfo->getPersistentInfo()->setDisableFurtherCompilation(false);
324+
325+
// TODO: Need to suspend diagnostic thread here
326+
327+
return true;
328+
}

0 commit comments

Comments
 (0)