Memoryclassloader: get loader constraint violation error

59 views
Skip to first unread message

lylyp...@gmail.com

unread,
Oct 17, 2014, 2:14:40 AM10/17/14
to jac...@googlegroups.com
Hi,
We are trying to integrate jacoco with our faultlocalization tool,
following your usage API example CoreTutorial.java.
Means we initilize a MemoryClassloader in which instrumented classes are redefined, then load classes from that classloader to execute in order for jacoco to be able to analyze.
It seems okay when we test a simple program, but for a quite more complicated one, we get an error about classloader.

This is the stacktrace:
java.lang.LinkageError: loader constraint violation: loader (instance of sun/misc/Launcher$AppClassLoader)
previously initiated loading for a different type with name "de/susebox/jtopas/PluginTokenizer"
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
at java.lang.Class.privateGetPublicMethods(Class.java:2547)
at java.lang.Class.privateGetPublicMethods(Class.java:2557)
at java.lang.Class.privateGetPublicMethods(Class.java:2557)
at java.lang.Class.getMethods(Class.java:1410)
at codecoverage.jacoco.JavaCoCo.extractTestCasesAsRequests(JavaCoCo.java:56)
at codecoverage.jacoco.JavaCoCo.run(JavaCoCo.java:107)

It looks like class "PluginTokenizer" was already loaded (and redefined) by MemoryClassloader, then by AppClassLoader, that cause violation.
Here are our classes: (I shortened by removing some unrelated lines)

45 public class JavaCoCo implements ICodeCoverage {
46 private Logger<?> logger = Logger.getDefaultLogger();
47 private static final String JUNIT_TEST_METHOD_PREFIX = "test";
48 private static final String JUNIT_TEST_SUITE_PREFIX = "suite";
49 private MemoryClassLoader memoryClassLoader;
50
51 private List<Request> extractTestCasesAsRequests(
52 List<String> junitClassNames) throws ClassNotFoundException {
53 ArrayList<Request> requests = new ArrayList<Request>();
54 for (String className : junitClassNames) {
55 Class<?> junitClass = memoryClassLoader.loadClass(className);
56 Method[] methods = junitClass.getMethods();
57 for (Method method : methods) {
58 Test test = method.getAnnotation(Test.class);
59 if (test != null) {
63 Request request = Request.method(junitClass, method.getName());
64 requests.add(request);
65 }
66 }
67 }
68 return requests;
69 }
70
71 public void run(ICoverageReport report, List<String> testingClassNames,
72 List<String> junitClassNames) throws Exception {
73 report.setTestingClassNames(testingClassNames);
74 List<String> classNameForJaCoco = new ArrayList<String>(testingClassNames);
75 classNameForJaCoco.addAll(junitClassNames);
76
77 // For instrumentation and runtime we need a IRuntime instance
78 // to collect execution data:
79 final IRuntime runtime = new LoggerRuntime();
80
81 memoryClassLoader = new MemoryClassLoader();
83 // The Instrumenter creates a modified version of our test target class
84 // that contains additional probes for execution data recording:
85 final Instrumenter instr = new Instrumenter(runtime);
86 ArrayList<byte[]> instrumenteds = new ArrayList<byte[]>();
87
88 for(String clazz: classNameForJaCoco){
89 try {
90 instrumenteds.add(instr.instrument(getTargetClass(clazz), clazz));
91 } catch (IOException e) {
92 logger.logEx(e, "cannot load class " + clazz);
93 throw e;
94 }
95 }
96
97 for(int j = 0; j < classNameForJaCoco.size(); j++){
98 String testingClassName = classNameForJaCoco.get(j);
99 memoryClassLoader.addDefinition(testingClassName, instrumenteds.get(j));
100 }
101
102 // Now we're ready to run our instrumented class and need to startup the
103 // runtime first:
104 final RuntimeData data = new RuntimeData();
105 runtime.startup(data);
106
107 List<Request> testcases = extractTestCasesAsRequests(junitClassNames);
108 for(int i = 0; i < testcases.size(); i++){
109 Request testcase = testcases.get(i);
110
111 data.reset();
112 final Class<?> targetClass = memoryClassLoader.loadClass(RequestExecution.class.getName());
113
114 // Here we execute our test target class through its Runnable interface:
115 final Runnable targetInstance = (Runnable) targetClass.newInstance();
116
117 Method setRequest = targetClass.getMethod("setRequest", Request.class);
118 setRequest.invoke(targetInstance, testcase);
119
120 targetInstance.run();
121
122 /* Extract the test's running result */
123 Method getResult = targetClass.getMethod("getResult");
124 boolean isPassed = (Boolean) getResult.invoke(targetInstance);
125 List<Failure> failures = RequestExecution.getFailureTrace(
126 targetClass, targetInstance);
127 report.addFailureTrace(extractBrkpsFromTrace(failures,
128 testingClassNames));
129
130 // At the end of test execution we collect execution data and shutdown
131 // the runtime:
132 final ExecutionDataStore executionData = new ExecutionDataStore();
133 final SessionInfoStore sessionInfos = new SessionInfoStore();
134 data.collect(executionData, sessionInfos, false);
135
136 // Together with the original class definition we can calculate coverage
137 // information:
138 final CoverageBuilder coverageBuilder = new CoverageBuilder();
139 final Analyzer analyzer = new Analyzer(executionData, coverageBuilder);
140 for(String testingClassName: testingClassNames){
141 analyzer.analyzeClass(getTargetClass(testingClassName), testingClassName);
142 }
143
144 // Let's dump some metrics and line coverage information:
145 for (final IClassCoverage cc : coverageBuilder.getClasses()) {
146 // do not display data for junit test file
147
161 }
162 }
163 runtime.shutdown();
164 }

191 }

MemoryClassLoader:

9 public class MemoryClassLoader extends ClassLoader {
10
11 private final Map<String, byte[]> definitions = new HashMap<String, byte[]>();
12
13 public MemoryClassLoader(ClassLoader parent) {
14 super(parent);
15 }
16
17 public MemoryClassLoader () {
18
19 }

29 public void addDefinition(final String name, final byte[] bytes) {
30 definitions.put(name, bytes);
31 }
32
33 @Override
34 protected Class<?> loadClass(final String name, final boolean resolve)
35 throws ClassNotFoundException {
36 final byte[] bytes = definitions.get(name);
37 if (bytes != null) {
38 Class<?> result = defineClass(name, bytes, 0, bytes.length);
39 // prevent defining class second time.
40 definitions.remove(name);
41 return result;
42 }
43 return super.loadClass(name, resolve);
44 }
45 }

RequestExecution is a runnable class to execute a junit method as a request.
input of the program:
testingClassNames:
"de.susebox.java.io.ExtIOException",
"de.susebox.java.lang.ExtIndexOutOfBoundsException",
"de.susebox.java.util.InputStreamTokenizer",
"de.susebox.jtopas.PluginTokenizer"

junitClassNames:
"de.susebox.java.util.TestTokenizerProperties",
"de.susebox.java.util.TestTokenProperties",
"de.susebox.java.util.TestInputStreamTokenizer",
"de.susebox.java.util.TestDifficultSituations",
"de.susebox.jtopas.TestPluginTokenizer",
"de.susebox.jtopas.TestTokenizerSpeed"

In this test, PluginTokenizer is called in both TestPluginTokenizer, and TestTokenizerSpeed

Do you have any idea about the problem and how to fix it?
Let me know if you need any more information.
Thanks in advance for the help,
lyly

Marc R. Hoffmann

unread,
Oct 17, 2014, 4:55:32 AM10/17/14
to jac...@googlegroups.com
Hi,

the memory class loader is a very simple implementation for a single
class. If you want to load multiple classes or even redefine them within
the JVM this takes a little more considerations. I recommend to first
get a very good understanding about class loading and class lifecycle.

My question is why do you want to do this at all? The JaCoCo agent
transparently instruments any class loaded by the JVM. Even those
classes which are created or redefined on the fly. It even offers a
runtime API to fetch execution data from within the JVM:

http://www.eclemma.org/jacoco/trunk/doc/api/org/jacoco/agent/rt/package-summary.html

Best regards,
-marc
Reply all
Reply to author
Forward
This conversation is locked
You cannot reply and perform actions on locked conversations.
0 new messages