From 4fc682265ce9e8db0101f978d32b142af6751493 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 18 Nov 2022 17:01:30 +0800 Subject: [PATCH] classloader command support jdk.internal.loader.ClassLoaders$AppClassLoade. #2350 --- .../command/klass100/ClassLoaderCommand.java | 40 +++++--------- .../arthas/core/util/ClassLoaderUtils.java | 55 +++++++++++++++++++ .../taobao/arthas/core/util/StringUtils.java | 13 +++++ 3 files changed, 83 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java index 757829198..3216e8b76 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java @@ -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 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 getClassLoaderUrls(ClassLoader classLoader) { - List urlStrs = new ArrayList(); - 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 urlStats(Instrumentation inst) { Map urlStats = new HashMap(); Map> usedUrlsMap = new HashMap>(); @@ -441,13 +427,17 @@ public class ClassLoaderCommand extends AnnotatedCommand { for (Entry> entry : usedUrlsMap.entrySet()) { ClassLoader loader = entry.getKey(); Set usedUrls = entry.getValue(); - List allUrls = getClassLoaderUrls(loader); + URL[] allUrls = ClassLoaderUtils.getUrls(loader); List unusedUrls = new ArrayList(); - 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; diff --git a/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java b/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java index aba374346..7be694ade 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java @@ -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 path = (ArrayList) unsafe.getObject(ucpObject, pathFieldOffset); + + return path.toArray(new URL[path.size()]); + } catch (Throwable e) { + // ignore + return null; + } + } + return null; + } } diff --git a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java index 1762c7df3..d243687b3 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java @@ -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 toStringList(URL[] urls) { + if (urls != null) { + List result = new ArrayList(urls.length); + for (URL url : urls) { + result.add(url.toString()); + } + return result; + } + return Collections.emptyList(); + } }