classloader command support jdk.internal.loader.ClassLoaders$AppClassLoade. #2350

This commit is contained in:
hengyunabc 2022-11-18 17:01:30 +08:00
parent fcfd8eeb62
commit 4fc682265c
3 changed files with 83 additions and 25 deletions

View File

@ -15,6 +15,7 @@ import com.taobao.arthas.core.shell.handlers.Handler;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.arthas.core.util.ClassLoaderUtils;
import com.taobao.arthas.core.util.ResultUtils;
import com.taobao.arthas.core.util.StringUtils;
import com.taobao.arthas.core.util.affect.RowAffect;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
@ -23,7 +24,6 @@ import com.taobao.middleware.cli.annotations.Summary;
import java.lang.instrument.Instrumentation;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
@ -246,14 +246,14 @@ public class ClassLoaderCommand extends AnnotatedCommand {
private void processClassLoader(CommandProcess process, Instrumentation inst, ClassLoader targetClassLoader) {
RowAffect affect = new RowAffect();
if (targetClassLoader != null) {
if (targetClassLoader instanceof URLClassLoader) {
List<String> classLoaderUrls = getClassLoaderUrls(targetClassLoader);
affect.rCnt(classLoaderUrls.size());
if (classLoaderUrls.isEmpty()) {
URL[] classLoaderUrls = ClassLoaderUtils.getUrls(targetClassLoader);
if (classLoaderUrls != null) {
affect.rCnt(classLoaderUrls.length);
if (classLoaderUrls.length == 0) {
process.appendResult(new MessageModel("urls is empty."));
} else {
process.appendResult(new ClassLoaderModel().setUrls(classLoaderUrls));
affect.rCnt(classLoaderUrls.size());
process.appendResult(new ClassLoaderModel().setUrls(StringUtils.toStringList(classLoaderUrls)));
affect.rCnt(classLoaderUrls.length);
}
} else {
process.appendResult(new MessageModel("not a URLClassLoader."));
@ -403,20 +403,6 @@ public class ClassLoaderCommand extends AnnotatedCommand {
}
}
private static List<String> getClassLoaderUrls(ClassLoader classLoader) {
List<String> urlStrs = new ArrayList<String>();
if (classLoader instanceof URLClassLoader) {
URLClassLoader cl = (URLClassLoader) classLoader;
URL[] urls = cl.getURLs();
if (urls != null) {
for (URL url : urls) {
urlStrs.add(url.toString());
}
}
}
return urlStrs;
}
private Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats(Instrumentation inst) {
Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats = new HashMap<ClassLoaderVO, ClassLoaderUrlStat>();
Map<ClassLoader, Set<String>> usedUrlsMap = new HashMap<ClassLoader, Set<String>>();
@ -441,13 +427,17 @@ public class ClassLoaderCommand extends AnnotatedCommand {
for (Entry<ClassLoader, Set<String>> entry : usedUrlsMap.entrySet()) {
ClassLoader loader = entry.getKey();
Set<String> usedUrls = entry.getValue();
List<String> allUrls = getClassLoaderUrls(loader);
URL[] allUrls = ClassLoaderUtils.getUrls(loader);
List<String> unusedUrls = new ArrayList<String>();
for (String url : allUrls) {
if (!usedUrls.contains(url)) {
unusedUrls.add(url);
if (allUrls != null) {
for (URL url : allUrls) {
String urlStr = url.toString();
if (!usedUrls.contains(urlStr)) {
unusedUrls.add(urlStr);
}
}
}
urlStats.put(ClassUtils.createClassLoaderVO(loader), new ClassLoaderUrlStat(usedUrls, unusedUrls));
}
return urlStats;

View File

@ -1,6 +1,9 @@
package com.taobao.arthas.core.util;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@ -120,4 +123,56 @@ public class ClassLoaderUtils {
}
return matchClassLoaders;
}
@SuppressWarnings({ "unchecked", "restriction" })
public static URL[] getUrls(ClassLoader classLoader) {
if (classLoader instanceof URLClassLoader) {
return ((URLClassLoader) classLoader).getURLs();
}
// jdk9
if (classLoader.getClass().getName().startsWith("jdk.internal.loader.ClassLoaders$")) {
try {
Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
sun.misc.Unsafe unsafe = (sun.misc.Unsafe) field.get(null);
Class<?> ucpOwner = classLoader.getClass();
Field ucpField = null;
// jdk 9~15: jdk.internal.loader.ClassLoaders$AppClassLoader.ucp
// jdk 16~17: jdk.internal.loader.BuiltinClassLoader.ucp
while (ucpField == null && !ucpOwner.getName().equals("java.lang.Object")) {
try {
ucpField = ucpOwner.getDeclaredField("ucp");
} catch (NoSuchFieldException ex) {
ucpOwner = ucpOwner.getSuperclass();
}
}
if (ucpField == null) {
return null;
}
long ucpFieldOffset = unsafe.objectFieldOffset(ucpField);
Object ucpObject = unsafe.getObject(classLoader, ucpFieldOffset);
if (ucpObject == null) {
return null;
}
// jdk.internal.loader.URLClassPath.path
Field pathField = ucpField.getType().getDeclaredField("path");
if (pathField == null) {
return null;
}
long pathFieldOffset = unsafe.objectFieldOffset(pathField);
ArrayList<URL> path = (ArrayList<URL>) unsafe.getObject(ucpObject, pathFieldOffset);
return path.toArray(new URL[path.size()]);
} catch (Throwable e) {
// ignore
return null;
}
}
return null;
}
}

View File

@ -20,9 +20,11 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Set;
@ -981,4 +983,15 @@ public abstract class StringUtils {
public static String beautifyName(String name) {
return name.replace(' ', '_').toLowerCase();
}
public static List<String> toStringList(URL[] urls) {
if (urls != null) {
List<String> result = new ArrayList<String>(urls.length);
for (URL url : urls) {
result.add(url.toString());
}
return result;
}
return Collections.emptyList();
}
}