├── .gitignore ├── LICENSE ├── README.md ├── README_ENUS.md ├── atp-client-api ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── aliyun │ │ └── atp │ │ └── tool │ │ ├── ATPClient.java │ │ └── ClientException.java │ └── test │ └── java │ └── Example.java ├── atp-client-tool ├── pom.xml └── src │ └── main │ └── java │ ├── com │ └── aliyun │ │ └── atp │ │ └── tool │ │ ├── Command.java │ │ ├── CommandOption.java │ │ ├── FullJcmdCommand.java │ │ ├── HeapDumpCommand.java │ │ ├── HeapHistogramCommand.java │ │ ├── HotSpotVM.java │ │ ├── JcmdCommand.java │ │ ├── Main.java │ │ └── ThreadDumpCommand.java │ └── sun │ └── tools │ └── attach │ ├── BsdVirtualMachine.java │ ├── LinuxVirtualMachine.java │ ├── VirtualMachineImpl.java │ └── WindowsVirtualMachine.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | .idea 3 | build/** 4 | *.bin 5 | artifact.jar 6 | atptool.jar 7 | *.iml 8 | target/** 9 | run.sh 10 | **/target/** 11 | # Should be genreated during compilation 12 | atp-client-api/src/main/resources/atp-client-tool.jar 13 | atp-client-api/src/main/atp-client-api 14 | .T* 15 | *.log 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Alibaba Cloud 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | > [中文](README.md) | [English](README_ENUS.md) 3 | 4 | 这是[阿里云应用诊断分析平台(ATP)](https://www.aliyun.com/product/developerservices/atp)的数据源生成工具,它可以帮助您生成Java堆转储(Heap dump)文件、Java线程栈日志等,它支持仅包含JRE的环境,以及AlpineJDK等特殊环境,可以运行在Windows/MacOS/Linux,目标虚拟机支持JDK6到JDK20。 5 | 6 | # 作为命令行工具使用 7 | 您可以编译、构建出jar包,并通过Java命令行直接执行它 8 | ```sh 9 | $ mvn package 10 | $ java -Xbootclasspath/a:atp-client-api/src/main/resources/atp-client-tool.jar -jar atp-client-api/src/main/resources/atp-client-tool.jar 11 | ``` 12 | 参数如下 13 | ```sh 14 | Usage: 15 | Main 16 | 17 | Subcommands: 18 | heap Generate heap dump of Java process 19 | [-file(mandatory), -object(mandatory)] 20 | thread Print all threads and their stack traces 21 | [-lock(optional), -extend(optional)] 22 | list_heap List class and number of instance in Java heap 23 | compiler_codeheap_analytics Print CodeHeap analytics 24 | compiler_codecache Print code cache layout and bounds. 25 | compiler_codelist Print all compiled methods in code cache that are alive 26 | compiler_directives_add Add compiler directives from file. 27 | compiler_directives_clear Remove all compiler directives. 28 | compiler_directives_print Print all active compiler directives. 29 | compiler_directives_remove Remove latest added compiler directive. 30 | compiler_queue Print methods queued for compilation. 31 | gc_class_histogram Provide statistics about the Java heap usage. 32 | gc_class_stats Provide statistics about Java class meta data. 33 | gc_finalizer_info Provide information about Java finalization queue. 34 | gc_heap_dump Generate a HPROF format dump of the Java heap. 35 | gc_heap_info Provide generic Java heap information. 36 | gc_run Call java.lang.System.gc(). 37 | gc_run_finalization Call java.lang.System.runFinalization(). 38 | jfr_check Checks running JFR recording(s) 39 | jfr_configure Configure JFR 40 | jfr_dump Copies contents of a JFR recording to file. Either the name or the recording id must be specified. 41 | jfr_start Starts a new JFR recording 42 | jfr_stop Stops a JFR recording 43 | jvmti_agent_load Load JVMTI native agent. 44 | jvmti_data_dump Signal the JVM to do a data-dump request for JVMTI. 45 | managementagent_start Start remote management agent. 46 | managementagent_start_local Start local management agent. 47 | managementagent_status Print the management agent status. 48 | managementagent_stop Stop remote management agent. 49 | thread_print Print all threads with stacktraces. 50 | vm_check_commercial_features Obsolete 51 | vm_class_hierarchy Print a list of all loaded classes, indented to show the class hiearchy. The name of each class is followed by the ClassLoaderData* of its ClassLoader, or "null" if loaded by the bootstrap class loader. 52 | vm_classloader_stats Print statistics about all ClassLoaders. 53 | vm_classloaders Prints classloader hierarchy. 54 | vm_command_line Print the command line used to start this VM instance. 55 | vm_dynlibs Print loaded dynamic libraries. 56 | vm_flags Print VM flag options and their current values. 57 | vm_info Print information about JVM environment and status. 58 | vm_log Lists current log configuration, enables/disables/configures a log output, or rotates all logs. 59 | vm_metaspace Prints the statistics for the metaspace 60 | vm_native_memory Print native memory usage 61 | vm_print_touched_methods Print all methods that have ever been touched during the lifetime of this JVM. 62 | vm_set_flag Sets VM flag option using the provided value. 63 | vm_stringtable Dump string table. 64 | vm_symboltable Dump symbol table. 65 | vm_system_properties Print system properties. 66 | vm_systemdictionary Prints the statistics for dictionary hashtable sizes and bucket length 67 | vm_unlock_commercial_features Obsolete 68 | vm_uptime Print VM uptime. 69 | vm_version Print JVM version information. 70 | 71 | ``` 72 | 73 | # 作为Java SDK使用 74 | 您可以添加如下Maven依赖并通过SDK方式调用: 75 | ``` 76 | 77 | com.aliyun.atp 78 | atp-client-api 79 | 1.0.4 80 | 81 | ``` 82 | ```java 83 | import com.aliyun.atp.tool.ATPClient; 84 | import com.aliyun.atp.tool.ClientException; 85 | 86 | public class SDKExample { 87 | public static void main(String[] args) { 88 | try { 89 | ATPClient.execute(null); 90 | ATPClient.execute(new String[]{"", "thread"}); 91 | ATPClient.execute(new String[]{"", "full_jcmd"}); 92 | ATPClient.execute(new String[]{"", "heap", "-file=/tmp/heapdump.hprof","-object=all"}); 93 | ATPClient.execute(new String[]{"", "heap", "-file=/tmp/heapdump.hprof","-object=live"}); 94 | } catch (ClientException e) { 95 | e.printStackTrace(); 96 | } 97 | } 98 | } 99 | ``` 100 | -------------------------------------------------------------------------------- /README_ENUS.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | > [中文](README.md) | [English](README_ENUS.md) 3 | 4 | This is a client of [Aliyun Application Troubleshooting Platform(ATP)](https://www.aliyun.com/product/developerservices/atp), it's able to generate Java heap dump/Java stack log files. 5 | It works well on `Windows/MacOS/Linux/Apline` and support a large range of JDK versions(`JRE6-JRE20`) 6 | 7 | ## Build 8 | ```sh 9 | $ mvn package 10 | $ java -Xbootclasspath/a:target/atp-client-1.0.jar -jar target/atp-client-1.0.jar 11 | ``` 12 | 13 | ## Usage 14 | ```sh 15 | Usage: 16 | ATPClient 17 | 18 | Subcommands: 19 | heap Generate heap dump of Java process 20 | [-file(mandatory), -object(mandatory)] 21 | thread Print all threads and their stack traces 22 | [-lock(optional), -extend(optional)] 23 | list_heap List class and number of instance in Java heap 24 | compiler_codeheap_analytics Print CodeHeap analytics 25 | compiler_codecache Print code cache layout and bounds. 26 | compiler_codelist Print all compiled methods in code cache that are alive 27 | compiler_directives_add Add compiler directives from file. 28 | compiler_directives_clear Remove all compiler directives. 29 | compiler_directives_print Print all active compiler directives. 30 | compiler_directives_remove Remove latest added compiler directive. 31 | compiler_queue Print methods queued for compilation. 32 | gc_class_histogram Provide statistics about the Java heap usage. 33 | gc_class_stats Provide statistics about Java class meta data. 34 | gc_finalizer_info Provide information about Java finalization queue. 35 | gc_heap_dump Generate a HPROF format dump of the Java heap. 36 | gc_heap_info Provide generic Java heap information. 37 | gc_run Call java.lang.System.gc(). 38 | gc_run_finalization Call java.lang.System.runFinalization(). 39 | jfr_check Checks running JFR recording(s) 40 | jfr_configure Configure JFR 41 | jfr_dump Copies contents of a JFR recording to file. Either the name or the recording id must be specified. 42 | jfr_start Starts a new JFR recording 43 | jfr_stop Stops a JFR recording 44 | jvmti_agent_load Load JVMTI native agent. 45 | jvmti_data_dump Signal the JVM to do a data-dump request for JVMTI. 46 | managementagent_start Start remote management agent. 47 | managementagent_start_local Start local management agent. 48 | managementagent_status Print the management agent status. 49 | managementagent_stop Stop remote management agent. 50 | thread_print Print all threads with stacktraces. 51 | vm_check_commercial_features Obsolete 52 | vm_class_hierarchy Print a list of all loaded classes, indented to show the class hiearchy. The name of each class is followed by the ClassLoaderData* of its ClassLoader, or "null" if loaded by the bootstrap class loader. 53 | vm_classloader_stats Print statistics about all ClassLoaders. 54 | vm_classloaders Prints classloader hierarchy. 55 | vm_command_line Print the command line used to start this VM instance. 56 | vm_dynlibs Print loaded dynamic libraries. 57 | vm_flags Print VM flag options and their current values. 58 | vm_info Print information about JVM environment and status. 59 | vm_log Lists current log configuration, enables/disables/configures a log output, or rotates all logs. 60 | vm_metaspace Prints the statistics for the metaspace 61 | vm_native_memory Print native memory usage 62 | vm_print_touched_methods Print all methods that have ever been touched during the lifetime of this JVM. 63 | vm_set_flag Sets VM flag option using the provided value. 64 | vm_stringtable Dump string table. 65 | vm_symboltable Dump symbol table. 66 | vm_system_properties Print system properties. 67 | vm_systemdictionary Prints the statistics for dictionary hashtable sizes and bucket length 68 | vm_unlock_commercial_features Obsolete 69 | vm_uptime Print VM uptime. 70 | vm_version Print JVM version information. 71 | 72 | ``` 73 | -------------------------------------------------------------------------------- /atp-client-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.aliyun.atp 8 | atp-sdk 9 | 1.0.4 10 | 11 | 12 | atp-client-api 13 | 14 | 15 | 8 16 | 8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | com.aliyun.atp 23 | atp-client-tool 24 | 1.0.4 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /atp-client-api/src/main/java/com/aliyun/atp/tool/ATPClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * Copyright (c) 2022, 2023 Alibaba Cloud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | package com.aliyun.atp.tool; 24 | 25 | import java.io.*; 26 | import java.nio.file.Files; 27 | import java.nio.file.StandardCopyOption; 28 | import java.util.ArrayList; 29 | import java.util.Arrays; 30 | import java.util.Random; 31 | import java.util.concurrent.TimeUnit; 32 | 33 | @SuppressWarnings("unused") 34 | public class ATPClient { 35 | private static final String ATP_CLIENT_TOOL_JAR = "atp-client-tool.jar"; 36 | private static final Random RAND = new Random(); 37 | 38 | private static String findJavaCommand() { 39 | String javaHome = System.getProperty("java.home"); 40 | if (javaHome != null && !javaHome.isEmpty()) { 41 | String jdkTool = javaHome + File.separator + "bin" + File.separator; 42 | if (isWindows()) { 43 | jdkTool += "java.exe"; 44 | } else { 45 | jdkTool += "java"; 46 | } 47 | return jdkTool; 48 | } 49 | return isWindows() ? "java.exe" : "java"; 50 | } 51 | 52 | private static boolean isWindows() { 53 | String os = System.getProperty("os.name").toLowerCase(); 54 | return os != null && os.contains("win"); 55 | } 56 | 57 | private static boolean isJavaCommandExist(String javaCmd) { 58 | try { 59 | Process process = Runtime.getRuntime().exec(javaCmd + " -version"); 60 | int exitValue = process.waitFor(); 61 | return exitValue == 0; 62 | } catch (IOException | InterruptedException e) { 63 | return false; 64 | } 65 | } 66 | 67 | private static File extractClientTool() throws ClientException { 68 | File file; 69 | try { 70 | file = File.createTempFile(".ATP", ATP_CLIENT_TOOL_JAR); 71 | } catch (IOException e) { 72 | throw new ClientException("Failed to create temporary file: " + e.getMessage()); 73 | } 74 | 75 | ClassLoader loader = ATPClient.class.getClassLoader(); 76 | if (loader == null) { 77 | throw new ClientException("Can not find client tool: it should not be loaded by bootclassloader"); 78 | } 79 | 80 | InputStream link = loader.getResourceAsStream(ATP_CLIENT_TOOL_JAR); 81 | if (link == null) { 82 | throw new ClientException("Can not find client tool"); 83 | } 84 | 85 | try { 86 | Files.copy(link, file.getAbsoluteFile().toPath(), StandardCopyOption.REPLACE_EXISTING); 87 | } catch (IOException e) { 88 | // fallback to create temporary file in working directory 89 | file = new File(".T" + RAND.nextInt() + ATP_CLIENT_TOOL_JAR); 90 | try { 91 | Files.copy(link, file.getAbsoluteFile().toPath(), StandardCopyOption.REPLACE_EXISTING); 92 | return file; 93 | } catch (IOException ex) { 94 | } 95 | throw new ClientException("Failed to extract client tool from jar: " + e.getMessage()); 96 | } 97 | 98 | return file; 99 | } 100 | 101 | public static Process startProcess(String javaCommand, String clientToolPath, String[] args) throws IOException { 102 | // launch client tool jar as standalone application 103 | ArrayList cmdArgs = new ArrayList<>(); 104 | cmdArgs.add(javaCommand); 105 | cmdArgs.add("-Xbootclasspath/a:" + clientToolPath); 106 | cmdArgs.add("-jar"); 107 | cmdArgs.add(clientToolPath); 108 | if (args != null) { 109 | cmdArgs.addAll(Arrays.asList(args)); 110 | } 111 | ProcessBuilder ps = new ProcessBuilder(cmdArgs); 112 | ps.redirectErrorStream(true); 113 | return ps.start(); 114 | } 115 | 116 | public static void execute(String[] args) throws ClientException { 117 | File clientTool = extractClientTool(); 118 | 119 | // before launching client tool, we need to check if "java" exists 120 | String javaCommand = findJavaCommand(); 121 | if (!isJavaCommandExist(javaCommand)) { 122 | throw new ClientException("Failed to execute client tool: can not find java command"); 123 | } 124 | 125 | try { 126 | Process pr = startProcess(javaCommand, clientTool.getAbsolutePath(), args); 127 | try (InputStreamReader ir = new InputStreamReader(pr.getInputStream()); 128 | BufferedReader br = new BufferedReader(ir)) { 129 | String line; 130 | while ((line = br.readLine()) != null) { 131 | System.out.println(line); 132 | } 133 | pr.waitFor(1, TimeUnit.HOURS); 134 | } 135 | } catch (IOException | InterruptedException ex) { 136 | throw new ClientException("Failed to execute client tool: " + ex.getMessage()); 137 | } finally { 138 | clientTool.delete(); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /atp-client-api/src/main/java/com/aliyun/atp/tool/ClientException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * Copyright (c) 2023 Alibaba Cloud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | package com.aliyun.atp.tool; 24 | 25 | public class ClientException extends Exception{ 26 | public ClientException(String message) { 27 | super(message); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /atp-client-api/src/test/java/Example.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * Copyright (c) 2023 Alibaba Cloud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | import com.aliyun.atp.tool.ATPClient; 25 | import com.aliyun.atp.tool.ClientException; 26 | import sun.management.VMManagement; 27 | 28 | import java.lang.management.ManagementFactory; 29 | import java.lang.management.RuntimeMXBean; 30 | import java.lang.reflect.Field; 31 | import java.lang.reflect.Method; 32 | 33 | public class Example { 34 | private static int getCurrentProcessId() throws Exception { 35 | 36 | RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); 37 | Field jvm = runtime.getClass().getDeclaredField("jvm"); 38 | jvm.setAccessible(true); 39 | 40 | VMManagement management = (VMManagement) jvm.get(runtime); 41 | Method method = management.getClass().getDeclaredMethod("getProcessId"); 42 | method.setAccessible(true); 43 | 44 | return (Integer) method.invoke(management); 45 | } 46 | 47 | public static void main(String[] args) { 48 | try { 49 | int pid = getCurrentProcessId(); 50 | ATPClient.execute(null); 51 | ATPClient.execute(new String[]{"" + pid, "thread"}); 52 | ATPClient.execute(new String[]{"" + pid, "heap", "-object=all", "-file=test.bin"}); 53 | } catch (ClientException e) { 54 | e.printStackTrace(); 55 | } catch (Exception e) { 56 | throw new RuntimeException(e); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /atp-client-tool/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.aliyun.atp 8 | atp-sdk 9 | 1.0.4 10 | 11 | 12 | atp-client-tool 13 | jar 14 | 15 | 16 | 17 | 18 | org.apache.maven.plugins 19 | maven-jar-plugin 20 | 2.3.1 21 | 22 | atp-client-tool 23 | 24 | 25 | com.aliyun.atp.tool.Main 26 | 27 | 28 | ${project.basedir}/../atp-client-api/src/main/resources 29 | 30 | 31 | 32 | 33 | 34 | 35 | 1.6 36 | 1.6 37 | UTF-8 38 | 39 | 40 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/com/aliyun/atp/tool/Command.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * Copyright (c) 2022 Alibaba Cloud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | package com.aliyun.atp.tool; 24 | 25 | import java.io.OutputStream; 26 | import java.io.PrintStream; 27 | import java.util.Arrays; 28 | 29 | abstract class Command { 30 | private final String name; 31 | private final String description; 32 | private final CommandOption[] options; 33 | private PrintStream newOut; 34 | 35 | 36 | public Command() { 37 | this.name = ""; 38 | this.description = ""; 39 | this.options = null; 40 | } 41 | 42 | public Command(String name) { 43 | this.name = name; 44 | this.description = ""; 45 | this.options = null; 46 | } 47 | 48 | public Command(String name, String description, CommandOption[] options) { 49 | this.name = name; 50 | this.description = description; 51 | this.options = options; 52 | } 53 | 54 | public String getName() { 55 | return name; 56 | } 57 | 58 | public CommandOption getOption(String name) { 59 | for (int i = 0; i < options.length; i++) { 60 | if (options[i].getName().equals(name)) { 61 | return options[i]; 62 | } 63 | } 64 | return null; 65 | } 66 | 67 | protected void parseInputArguments(String[] args) throws Exception { 68 | for (int i = 0; options != null && i < options.length; i++) { 69 | CommandOption option = options[i]; 70 | if (option.isMandatory()) { 71 | boolean found = false; 72 | for (int k = 0; k < args.length; k++) { 73 | if (args[k].startsWith(option.getName())) { 74 | found = true; 75 | option.setValue(args[k]); 76 | break; 77 | } 78 | } 79 | if (!found) { 80 | throw new Exception("Option " + option.getName() + " is mandatory"); 81 | } 82 | } else { 83 | for (int k = 0; k < args.length; k++) { 84 | if (args[i].startsWith(option.getName())) { 85 | option.setValue(args[i]); 86 | break; 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | public Command redirectOutput(PrintStream os) { 94 | newOut = os; 95 | return this; 96 | } 97 | 98 | public final void executeCommand(HotSpotVM vm, String[] args) throws Exception { 99 | parseInputArguments(args); 100 | PrintStream oldOut = null; 101 | if (newOut != null) { 102 | oldOut = System.out; 103 | System.setOut(newOut); 104 | } 105 | try { 106 | execute(vm, args); 107 | } finally { 108 | if (oldOut != null) { 109 | System.setOut(oldOut); 110 | } 111 | } 112 | } 113 | 114 | protected abstract void execute(HotSpotVM vm, String[] args) throws Exception; 115 | 116 | @Override 117 | public String toString() { 118 | String str = String.format("%-30s%s", name, description != null ? description : ""); 119 | if (options != null && options.length > 0) { 120 | str += "\n " + Arrays.toString(options); 121 | } 122 | return str; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/com/aliyun/atp/tool/CommandOption.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * Copyright (c) 2022 Alibaba Cloud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | package com.aliyun.atp.tool; 24 | 25 | import java.util.Arrays; 26 | 27 | class CommandOption { 28 | private final String name; 29 | private final String[] valueSet; 30 | private final boolean mandatory; 31 | private String value; 32 | 33 | public CommandOption(String name, String defaultValue, boolean mandatory, String[] valueSet) { 34 | this.name = name; 35 | this.value = defaultValue; 36 | this.mandatory = mandatory; 37 | this.valueSet = valueSet; 38 | } 39 | 40 | private static String extractValue(String arg) { 41 | // e.g. -opt=value => "value" 42 | // -opt => "" 43 | if (arg.contains("=")) { 44 | return arg.split("=")[1]; 45 | } else { 46 | return ""; 47 | } 48 | } 49 | 50 | public String getValue() { 51 | return value; 52 | } 53 | 54 | public void setValue(String value) throws Exception { 55 | String userValue = extractValue(value); 56 | if (valueSet != null && valueSet.length > 0) { 57 | boolean match = false; 58 | for (int i = 0; i < valueSet.length; i++) { 59 | if (valueSet[i].equals(userValue)) { 60 | match = true; 61 | break; 62 | } 63 | } 64 | if (!match) { 65 | throw new Exception("Option " + name + " accepts " + Arrays.toString(valueSet) + " but got " + userValue); 66 | } 67 | } 68 | this.value = userValue; 69 | } 70 | 71 | public String getName() { 72 | return name; 73 | } 74 | 75 | public boolean isMandatory() { 76 | return mandatory; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | String opt = name; 82 | if (valueSet != null && valueSet.length > 0) { 83 | opt += "=" + Arrays.toString(valueSet); 84 | } 85 | opt += "("; 86 | opt += mandatory ? "mandatory" : "optional"; 87 | opt += ")"; 88 | return opt; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/com/aliyun/atp/tool/FullJcmdCommand.java: -------------------------------------------------------------------------------- 1 | package com.aliyun.atp.tool; 2 | 3 | import java.io.*; 4 | import java.util.Arrays; 5 | 6 | public class FullJcmdCommand extends Command { 7 | private static final String[] VALID = { 8 | "Compiler.CodeHeap_Analytics", 9 | "Compiler.codecache", 10 | "Compiler.codelist", 11 | "Compiler.directives_print", 12 | "Compiler.queue", 13 | "GC.class_histogram", 14 | "GC.class_stats", 15 | "GC.finalizer_info", 16 | "GC.heap_info", 17 | "JVMTI.data_dump", 18 | "Thread.print", 19 | "VM.class_hierarchy", 20 | "VM.classloader_stats", 21 | "VM.classloaders", 22 | "VM.command_line", 23 | "VM.dynlibs", 24 | "VM.flags", 25 | "VM.info", 26 | "VM.log", 27 | "VM.metaspace", 28 | "VM.native_memory", 29 | "VM.print_touched_methods", 30 | "VM.stringtable", 31 | "VM.symboltable", 32 | "VM.system_properties", 33 | "VM.systemdictionary", 34 | "VM.uptime", 35 | "VM.version", 36 | }; 37 | 38 | FullJcmdCommand(String name, String description) { 39 | super(name, description, new CommandOption[]{ 40 | new CommandOption("-file", "jcmd.log", false, null), 41 | }); 42 | } 43 | 44 | @Override 45 | protected void execute(HotSpotVM vm, String[] args) throws Exception { 46 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 47 | FileOutputStream fos = null; 48 | try { 49 | new JcmdCommand("", "help", "") 50 | .redirectOutput(new PrintStream(bos)) 51 | .executeCommand(vm, null); 52 | fos = new FileOutputStream(getOption("-file").getValue()); 53 | String availableJcmd = bos.toString(); 54 | String[] lines = availableJcmd.split("\n"); 55 | for (int i = 0; i < lines.length; i++) { 56 | if (lines[i].matches("[a-zA-Z_]+?\\.[a-zA-Z_]+")) { 57 | for (String valid : VALID) { 58 | if (lines[i].contains(valid)) { 59 | String head = "#LN_START#" + lines[i] + "#LN_END#\n"; 60 | fos.write(head.getBytes()); 61 | // Read help document of jcmd subcommands 62 | new JcmdCommand("", lines[i], "") 63 | .redirectOutput(new PrintStream(fos)) 64 | .executeCommand(vm, null); 65 | } 66 | } 67 | } 68 | } 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | // Failed to execute Jcmd operations 72 | // Skip... 73 | } finally { 74 | try { 75 | bos.close(); 76 | } catch (IOException e) { 77 | throw new RuntimeException(e); 78 | } 79 | if (fos != null) { 80 | fos.close(); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/com/aliyun/atp/tool/HeapDumpCommand.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * Copyright (c) 2022 Alibaba Cloud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | package com.aliyun.atp.tool; 24 | 25 | class HeapDumpCommand extends Command { 26 | private static final String VM_OPERATION_HEAP_DUMP = "dumpheap"; 27 | 28 | HeapDumpCommand(String commandName, String description) { 29 | super(commandName, description, new CommandOption[]{ 30 | new CommandOption("-file", "", true, null), 31 | new CommandOption("-object", "", true, new String[]{"all", "live"}), 32 | }); 33 | } 34 | 35 | @Override 36 | protected void execute(HotSpotVM vm, String[] args) throws Exception { 37 | String filePath = getOption("-file").getValue(); 38 | String dumpOption = getOption("-object").getValue(); 39 | vm.execute(VM_OPERATION_HEAP_DUMP, filePath, "-" + dumpOption); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/com/aliyun/atp/tool/HeapHistogramCommand.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * Copyright (c) 2022 Alibaba Cloud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | package com.aliyun.atp.tool; 24 | 25 | class HeapHistogramCommand extends Command { 26 | private static final String VM_OPERATION_INSPECT_HEAP = "inspectheap"; 27 | 28 | HeapHistogramCommand(String commandName, String description) { 29 | super(commandName, description, new CommandOption[]{}); 30 | } 31 | 32 | @Override 33 | protected void execute(HotSpotVM vm, String[] args) throws Exception { 34 | vm.execute(VM_OPERATION_INSPECT_HEAP); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/com/aliyun/atp/tool/HotSpotVM.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. 2 | * Copyright (c) 2022, Alibaba Group Holding Limited. All Rights Reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package com.aliyun.atp.tool; 26 | 27 | import sun.tools.attach.BsdVirtualMachine; 28 | import sun.tools.attach.LinuxVirtualMachine; 29 | import sun.tools.attach.VirtualMachineImpl; 30 | import sun.tools.attach.WindowsVirtualMachine; 31 | 32 | import java.io.*; 33 | import java.lang.reflect.Constructor; 34 | import java.util.Random; 35 | 36 | // Do not use advance syntax and JDK methods, this class is expected to work with JRE6 37 | @SuppressWarnings("unused") 38 | public abstract class HotSpotVM { 39 | private static final String OS_NAME = System.getProperty("os.name"); 40 | private static final String JAVA_VERSION = System.getProperty("java.version"); 41 | private final static String PROTOCOL_VERSION = "1"; 42 | private final static String TMP_DIR = "/tmp"; 43 | private final static int ATTACH_ERROR_BAD_VERSION = 101; 44 | 45 | private static boolean isMacOS() { 46 | return OS_NAME.toLowerCase().contains("mac"); 47 | } 48 | 49 | private static boolean isWindows() { 50 | return OS_NAME.toLowerCase().contains("win"); 51 | } 52 | 53 | private static boolean isLinux() { 54 | return OS_NAME.toLowerCase().contains("nux"); 55 | } 56 | 57 | public static HotSpotVM creatHotSpotVM(int pid) throws Exception { 58 | System.loadLibrary("attach"); 59 | 60 | String expectedVM; 61 | expectedVM = (getJreVersion() < 9 ? "Old" : "New"); 62 | expectedVM += isMacOS() ? "Bsd" : ""; 63 | expectedVM += isLinux() ? "Linux" : ""; 64 | expectedVM += isWindows() ? "Windows" : ""; 65 | expectedVM += "HotSpotVM"; 66 | Class[] vmSet = HotSpotVM.class.getDeclaredClasses(); 67 | for (Class vm : vmSet) { 68 | if (vm.getSimpleName().equals(expectedVM)) { 69 | Constructor ctor = 70 | (Constructor) vm.getConstructor(int.class/*pid*/); 71 | return ctor.newInstance(pid); 72 | } 73 | } 74 | throw new Exception("Can not find expected VM for JRE:" + getJreVersion() + ", OS:" + OS_NAME); 75 | } 76 | 77 | private static int getJreVersion() { 78 | String version = JAVA_VERSION; 79 | if (version.startsWith("1.")) { 80 | version = version.substring(2, 3); 81 | } else { 82 | int dot = version.indexOf("."); 83 | if (dot != -1) { 84 | version = version.substring(0, dot); 85 | } 86 | } 87 | return Integer.parseInt(version); 88 | } 89 | 90 | protected static void readRemaining(InputStream sis) throws IOException { 91 | byte[] b = new byte[256]; 92 | int n; 93 | do { 94 | n = sis.read(b); 95 | if (n > 0) { 96 | String s = new String(b, 0, n, "UTF-8"); 97 | System.out.print(s); 98 | } 99 | } while (n > 0); 100 | sis.close(); 101 | } 102 | 103 | protected static String readErrorMessage(InputStream sis) throws IOException { 104 | byte[] b = new byte[1024]; 105 | int n; 106 | StringBuilder message = new StringBuilder(); 107 | while ((n = sis.read(b)) != -1) { 108 | message.append(new String(b, 0, n, "UTF-8")); 109 | } 110 | return message.toString(); 111 | } 112 | 113 | protected static int readInt(InputStream sis) throws IOException { 114 | StringBuilder sb = new StringBuilder(); 115 | 116 | // read to \n or EOF 117 | int n; 118 | byte[] buf = new byte[1]; 119 | do { 120 | n = sis.read(buf, 0, 1); 121 | if (n > 0) { 122 | char c = (char) buf[0]; 123 | if (c == '\n') { 124 | break; 125 | } else { 126 | sb.append(c); 127 | } 128 | } 129 | } while (n > 0); 130 | 131 | if (sb.length() == 0) { 132 | throw new IOException("Premature EOF"); 133 | } 134 | 135 | int value; 136 | try { 137 | value = Integer.parseInt(sb.toString()); 138 | } catch (NumberFormatException x) { 139 | throw new IOException("Non-numeric value found - int expected"); 140 | } 141 | return value; 142 | } 143 | 144 | private static int readOneByte(InputStream is) throws IOException { 145 | byte[] b = new byte[1]; 146 | int n = is.read(b, 0, 1); 147 | if (n == 1) { 148 | return b[0] & 0xff; 149 | } else { 150 | return -1; 151 | } 152 | } 153 | 154 | private static void checkBeforeRead(byte[] bs, int off, int len) { 155 | if ((off < 0) || (off > bs.length) || (len < 0) || 156 | ((off + len) > bs.length) || ((off + len) < 0)) { 157 | throw new IndexOutOfBoundsException(); 158 | } 159 | } 160 | 161 | private static void checkExecReturn(IOException ioe, String cmd, InputStream sis) throws Exception { 162 | int completionStatus; 163 | try { 164 | completionStatus = readInt(sis); 165 | } catch (IOException x) { 166 | sis.close(); 167 | if (ioe != null) { 168 | throw ioe; 169 | } else { 170 | throw x; 171 | } 172 | } 173 | 174 | if (completionStatus != 0) { 175 | String message = readErrorMessage(sis); 176 | sis.close(); 177 | if (completionStatus == ATTACH_ERROR_BAD_VERSION) { 178 | throw new IOException("Invalid protocol when interacting with attach listener"); 179 | } 180 | if (cmd.equals("load")) { 181 | throw new Exception("Failed to load agent library due to" + message); 182 | } else { 183 | throw new Exception("Failed to execute command " + cmd + " " + message); 184 | } 185 | } 186 | } 187 | 188 | private static void writeString(int fd, String s) throws Exception { 189 | if (s.length() > 0) { 190 | byte[] b; 191 | b = s.getBytes("UTF-8"); 192 | if (getJreVersion() < 9) { 193 | if (isMacOS()) { 194 | BsdVirtualMachine.write(fd, b, 0, b.length); 195 | } else if (isLinux()) { 196 | LinuxVirtualMachine.write(fd, b, 0, b.length); 197 | } else { 198 | throw new Exception("Should not reach here"); 199 | } 200 | } else { 201 | if (isWindows()) { 202 | throw new Exception("Should not reach here"); 203 | } 204 | VirtualMachineImpl.write(fd, b, 0, b.length); 205 | } 206 | } 207 | byte[] b = new byte[1]; 208 | if (getJreVersion() < 9) { 209 | if (isMacOS()) { 210 | BsdVirtualMachine.write(fd, b, 0, 1); 211 | } else if (isLinux()) { 212 | LinuxVirtualMachine.write(fd, b, 0, 1); 213 | } else { 214 | throw new Exception("Should not reach here"); 215 | } 216 | } else { 217 | if (isWindows()) { 218 | throw new Exception("Should not reach here"); 219 | } 220 | VirtualMachineImpl.write(fd, b, 0, 1); 221 | } 222 | } 223 | 224 | private static IOException writeCommand(int fd, String cmd, Object... args) throws Exception { 225 | IOException ioe = null; 226 | 227 | try { 228 | writeString(fd, PROTOCOL_VERSION); 229 | writeString(fd, cmd); 230 | 231 | for (int i = 0; i < 3; i++) { 232 | if (i < args.length && args[i] != null) { 233 | writeString(fd, (String) args[i]); 234 | } else { 235 | writeString(fd, ""); 236 | } 237 | } 238 | } catch (IOException x) { 239 | ioe = x; 240 | } 241 | return ioe; 242 | } 243 | 244 | public abstract void detach() throws IOException; 245 | 246 | public abstract void execute(String cmd, Object... args) throws Exception; 247 | 248 | private static class OldWindowsHotSpotVM extends HotSpotVM { 249 | private static final byte[] stub; 250 | 251 | static { 252 | WindowsVirtualMachine.init(); 253 | stub = WindowsVirtualMachine.generateStub(); 254 | } 255 | 256 | private volatile long proc; 257 | 258 | public OldWindowsHotSpotVM(int pid) throws Exception { 259 | proc = WindowsVirtualMachine.openProcess(pid); 260 | // The target VM might be a pre-6.0 VM so we enqueue a "null" command 261 | try { 262 | WindowsVirtualMachine.enqueue(proc, stub, null, null); 263 | } catch (IOException x) { 264 | throw new Exception(x.getMessage()); 265 | } 266 | } 267 | 268 | public void detach() throws IOException { 269 | if (proc != -1) { 270 | WindowsVirtualMachine.closeProcess(proc); 271 | proc = -1; 272 | } 273 | } 274 | 275 | public void execute(String cmd, Object... args) throws Exception { 276 | int r = (new Random()).nextInt(); 277 | String pipeName = "\\\\.\\pipe\\javatool" + r; 278 | long hPipe = WindowsVirtualMachine.createPipe(pipeName); 279 | 280 | if (proc == -1) { 281 | WindowsVirtualMachine.closePipe(hPipe); 282 | throw new IOException("Detached from target VM"); 283 | } 284 | 285 | try { 286 | WindowsVirtualMachine.enqueue(proc, stub, cmd, pipeName, args); 287 | WindowsVirtualMachine.connectPipe(hPipe); 288 | OldWindowsPipedInputStream is = new OldWindowsPipedInputStream(hPipe); 289 | HotSpotVM.checkExecReturn(null, cmd, is); 290 | HotSpotVM.readRemaining(is); 291 | } catch (IOException ioe) { 292 | WindowsVirtualMachine.closePipe(hPipe); 293 | throw ioe; 294 | } 295 | } 296 | 297 | private static class OldWindowsPipedInputStream extends InputStream { 298 | private long hPipe; 299 | 300 | public OldWindowsPipedInputStream(long hPipe) { 301 | this.hPipe = hPipe; 302 | } 303 | 304 | public synchronized int read() throws IOException { 305 | return HotSpotVM.readOneByte(this); 306 | } 307 | 308 | public synchronized int read(byte[] bs, int off, int len) throws IOException { 309 | if (len == 0) { 310 | return 0; 311 | } 312 | checkBeforeRead(bs, off, len); 313 | return WindowsVirtualMachine.readPipe(hPipe, bs, off, len); 314 | } 315 | 316 | public void close() throws IOException { 317 | if (hPipe != -1) { 318 | WindowsVirtualMachine.closePipe(hPipe); 319 | hPipe = -1; 320 | } 321 | } 322 | } 323 | } 324 | 325 | private static class OldBsdHotSpotVM extends HotSpotVM { 326 | private static final String tmpdir; 327 | 328 | static { 329 | tmpdir = BsdVirtualMachine.getTempDir(); 330 | } 331 | 332 | private String path; 333 | 334 | public OldBsdHotSpotVM(int pid) throws Exception { 335 | path = findSocketFile(pid); 336 | if (path == null) { 337 | File f = new File(tmpdir, ".attach_pid" + pid); 338 | BsdVirtualMachine.createAttachFile(f.getPath()); 339 | try { 340 | BsdVirtualMachine.sendQuitTo(pid); 341 | for (int i = 0; i < 5; i++) { 342 | try { 343 | Thread.sleep(1000); 344 | } catch (Throwable r) { 345 | } 346 | path = findSocketFile(pid); 347 | } 348 | if (path == null) { 349 | throw new Exception("Unable to open socket file after many retries"); 350 | } 351 | } finally { 352 | f.delete(); 353 | } 354 | } 355 | 356 | BsdVirtualMachine.checkPermissions(path); 357 | int s = BsdVirtualMachine.socket(); 358 | try { 359 | BsdVirtualMachine.connect(s, path); 360 | } finally { 361 | BsdVirtualMachine.close(s); 362 | } 363 | } 364 | 365 | public void detach() throws IOException { 366 | if (this.path != null) { 367 | this.path = null; 368 | } 369 | } 370 | 371 | public void execute(String cmd, Object... args) throws Exception { 372 | int s = BsdVirtualMachine.socket(); 373 | 374 | try { 375 | BsdVirtualMachine.connect(s, path); 376 | } catch (IOException x) { 377 | BsdVirtualMachine.close(s); 378 | throw x; 379 | } 380 | IOException ioe = HotSpotVM.writeCommand(s, cmd, args); 381 | OldBsdSocketInputStream sis = new OldBsdSocketInputStream(s); 382 | HotSpotVM.checkExecReturn(ioe, cmd, sis); 383 | HotSpotVM.readRemaining(sis); 384 | } 385 | 386 | private String findSocketFile(int pid) { 387 | String fn = ".java_pid" + pid; 388 | File f = new File(tmpdir, fn); 389 | return f.exists() ? f.getPath() : null; 390 | } 391 | 392 | private static class OldBsdSocketInputStream extends InputStream { 393 | private final int s; 394 | 395 | public OldBsdSocketInputStream(int s) { 396 | this.s = s; 397 | } 398 | 399 | public synchronized int read() throws IOException { 400 | return HotSpotVM.readOneByte(this); 401 | } 402 | 403 | public synchronized int read(byte[] bs, int off, int len) throws IOException { 404 | if (len == 0) { 405 | return 0; 406 | } 407 | checkBeforeRead(bs, off, len); 408 | return BsdVirtualMachine.read(s, bs, off, len); 409 | } 410 | 411 | public void close() throws IOException { 412 | BsdVirtualMachine.close(s); 413 | } 414 | } 415 | } 416 | 417 | static class OldLinuxHotSpotVM extends HotSpotVM { 418 | private String path; 419 | 420 | public OldLinuxHotSpotVM(int pid) 421 | throws Exception { 422 | 423 | path = findSocketFile(pid); 424 | if (path == null) { 425 | File f = createAttachFile(pid); 426 | try { 427 | // NOTE, OpenJDK does not officially support LinuxThreads anymore, and has not been tested for 428 | // LinuxThreads since a long time (so support for it is probably broken by now anyway). So here 429 | // WE DO NOT SUPPORT LinuxThread as well for alpine JDK8 430 | // https://bugs.openjdk.org/browse/JDK-8078513 431 | LinuxVirtualMachine.sendQuitTo(pid); 432 | 433 | for (int i = 0; i < 5; i++) { 434 | try { 435 | Thread.sleep(1000); 436 | } catch (Throwable r) { 437 | } 438 | path = findSocketFile(pid); 439 | } 440 | if (path == null) { 441 | throw new Exception("Unable to open socket file"); 442 | } 443 | } finally { 444 | f.delete(); 445 | } 446 | } 447 | 448 | LinuxVirtualMachine.checkPermissions(path); 449 | int s = LinuxVirtualMachine.socket(); 450 | try { 451 | LinuxVirtualMachine.connect(s, path); 452 | } finally { 453 | LinuxVirtualMachine.close(s); 454 | } 455 | } 456 | 457 | public void detach() throws IOException { 458 | if (this.path != null) { 459 | this.path = null; 460 | } 461 | } 462 | 463 | public void execute(String cmd, Object... args) throws Exception { 464 | int s = LinuxVirtualMachine.socket(); 465 | 466 | try { 467 | LinuxVirtualMachine.connect(s, path); 468 | } catch (IOException x) { 469 | LinuxVirtualMachine.close(s); 470 | throw x; 471 | } 472 | IOException ioe = HotSpotVM.writeCommand(s, cmd, args); 473 | OldLinuxSocketInputStream sis = new OldLinuxSocketInputStream(s); 474 | HotSpotVM.checkExecReturn(ioe, cmd, sis); 475 | HotSpotVM.readRemaining(sis); 476 | } 477 | 478 | private String findSocketFile(int pid) { 479 | File f = new File(TMP_DIR, ".java_pid" + pid); 480 | if (!f.exists()) { 481 | return null; 482 | } 483 | return f.getPath(); 484 | } 485 | 486 | private File createAttachFile(int pid) throws IOException { 487 | String fn = ".attach_pid" + pid; 488 | String path = "/proc/" + pid + "/cwd/" + fn; 489 | File f = new File(path); 490 | try { 491 | f.createNewFile(); 492 | } catch (IOException x) { 493 | f = new File(TMP_DIR, fn); 494 | f.createNewFile(); 495 | } 496 | return f; 497 | } 498 | 499 | private static class OldLinuxSocketInputStream extends InputStream { 500 | private final int fd; 501 | 502 | public OldLinuxSocketInputStream(int s) { 503 | this.fd = s; 504 | } 505 | 506 | public synchronized int read() throws IOException { 507 | return HotSpotVM.readOneByte(this); 508 | } 509 | 510 | public synchronized int read(byte[] bs, int off, int len) throws IOException { 511 | if (len == 0) { 512 | return 0; 513 | } 514 | checkBeforeRead(bs, off, len); 515 | return LinuxVirtualMachine.read(fd, bs, off, len); 516 | } 517 | 518 | public void close() throws IOException { 519 | LinuxVirtualMachine.close(fd); 520 | } 521 | } 522 | } 523 | 524 | private static class NewLinuxHotSpotVM extends HotSpotVM { 525 | private String socketPath; 526 | 527 | public NewLinuxHotSpotVM(int pid) throws Exception { 528 | int ns_pid = getNamespacePid(pid); 529 | File socket_file = findSocketFile(pid, ns_pid); 530 | socketPath = socket_file.getPath(); 531 | if (!socket_file.exists()) { 532 | File f = createAttachFile(pid, ns_pid); 533 | try { 534 | VirtualMachineImpl.sendQuitTo(pid); 535 | for (int i = 0; i < 5; i++) { 536 | Thread.sleep(1000); 537 | if (i >= 2 && !socket_file.exists()) { 538 | VirtualMachineImpl.sendQuitTo(pid); 539 | } 540 | } 541 | if (!socket_file.exists()) { 542 | throw new Exception("Unable to open socket file " + socket_file); 543 | } 544 | } finally { 545 | f.delete(); 546 | } 547 | } 548 | VirtualMachineImpl.checkPermissions(socketPath); 549 | VirtualMachineImpl.checkConnection(socketPath); 550 | } 551 | 552 | public void detach() throws IOException { 553 | if (socketPath != null) { 554 | socketPath = null; 555 | } 556 | } 557 | 558 | public void execute(String cmd, Object... args) throws Exception { 559 | int fd = VirtualMachineImpl.connectToVM(socketPath); 560 | IOException ioe = HotSpotVM.writeCommand(fd, cmd, args); 561 | NewLinuxSocketInputStream sis = new NewLinuxSocketInputStream(fd); 562 | HotSpotVM.checkExecReturn(ioe, cmd, sis); 563 | HotSpotVM.readRemaining(sis); 564 | } 565 | 566 | private File findSocketFile(int pid, int ns_pid) { 567 | String root = "/proc/" + pid + "/root/" + TMP_DIR; 568 | return new File(root, ".java_pid" + ns_pid); 569 | } 570 | 571 | private File createAttachFile(int pid, int nsPid) throws IOException { 572 | String fn = ".attach_pid" + nsPid; 573 | String path = "/proc/" + pid + "/cwd/" + fn; 574 | File f = new File(path); 575 | try { 576 | f.createNewFile(); 577 | } catch (IOException x) { 578 | String root; 579 | if (pid != nsPid) { 580 | root = "/proc/" + pid + "/root/" + TMP_DIR; 581 | } else { 582 | root = TMP_DIR; 583 | } 584 | f = new File(root, fn); 585 | f.createNewFile(); 586 | } 587 | return f; 588 | } 589 | 590 | private int getNamespacePid(int pid) throws Exception { 591 | String statusFile = "/proc/" + pid + "/status"; 592 | File f = new File(statusFile); 593 | if (!f.exists()) { 594 | return pid; 595 | } 596 | 597 | BufferedReader br = null; 598 | FileReader fr = null; 599 | try { 600 | fr = new FileReader(f); 601 | br = new BufferedReader(fr); 602 | String line; 603 | while ((line = br.readLine()) != null) { 604 | String[] parts = line.split(":"); 605 | if (parts.length == 2 && parts[0].trim().equals("NSpid")) { 606 | parts = parts[1].trim().split("\\s+"); 607 | return Integer.parseInt(parts[parts.length - 1]); 608 | } 609 | } 610 | return pid; 611 | } catch (NumberFormatException x) { 612 | throw new Exception("Unable to parse namespace"); 613 | } catch (IOException ioe) { 614 | throw new Exception("Unable to parse namespace"); 615 | } finally { 616 | try { 617 | if (fr != null) { 618 | 619 | fr.close(); 620 | } 621 | if (br != null) { 622 | br.close(); 623 | } 624 | } catch (IOException ioe1) { 625 | throw new Exception("Unable to close stream when parsing NSpid"); 626 | } 627 | } 628 | } 629 | 630 | private static class NewLinuxSocketInputStream extends InputStream { 631 | private final int sock; 632 | 633 | public NewLinuxSocketInputStream(int s) { 634 | this.sock = s; 635 | } 636 | 637 | public synchronized int read() throws IOException { 638 | return HotSpotVM.readOneByte(this); 639 | } 640 | 641 | public synchronized int read(byte[] bs, int off, int len) throws IOException { 642 | if (len == 0) { 643 | return 0; 644 | } 645 | checkBeforeRead(bs, off, len); 646 | return VirtualMachineImpl.read(sock, bs, off, len); 647 | } 648 | 649 | public void close() throws IOException { 650 | VirtualMachineImpl.close(sock); 651 | } 652 | } 653 | } 654 | 655 | static class NewBsdHotSpotVM extends HotSpotVM { 656 | private static final String tmpdir; 657 | 658 | static { 659 | tmpdir = VirtualMachineImpl.getTempDir(); 660 | } 661 | 662 | String socketFile; 663 | 664 | public NewBsdHotSpotVM(int pid) throws Exception { 665 | File socketFile = new File(tmpdir, ".java_pid" + pid); 666 | this.socketFile = socketFile.getPath(); 667 | if (!socketFile.exists()) { 668 | File f = createAttachFile(pid); 669 | try { 670 | VirtualMachineImpl.sendQuitTo(pid); 671 | for (int i = 0; i < 5; i++) { 672 | Thread.sleep(1000); 673 | if (i >= 2 && !socketFile.exists()) { 674 | VirtualMachineImpl.sendQuitTo(pid); 675 | } 676 | } 677 | if (!socketFile.exists()) { 678 | throw new Exception("Unable to open socket file " + socketFile); 679 | } 680 | } finally { 681 | f.delete(); 682 | } 683 | } 684 | 685 | VirtualMachineImpl.checkPermissions(this.socketFile); 686 | VirtualMachineImpl.checkConnection(this.socketFile); 687 | } 688 | 689 | public void detach() throws IOException { 690 | if (socketFile != null) { 691 | socketFile = null; 692 | } 693 | } 694 | 695 | public void execute(String cmd, Object... args) throws Exception { 696 | int fd = VirtualMachineImpl.connectToVM(socketFile); 697 | IOException ioe = HotSpotVM.writeCommand(fd, cmd, args); 698 | NewBsdSocketInputStream sis = new NewBsdSocketInputStream(fd); 699 | HotSpotVM.checkExecReturn(ioe, cmd, sis); 700 | HotSpotVM.readRemaining(sis); 701 | } 702 | 703 | private File createAttachFile(int pid) { 704 | File f = new File(tmpdir, ".attach_pid" + pid); 705 | VirtualMachineImpl.createAttachFile0(f.getPath()); 706 | return f; 707 | } 708 | 709 | private static class NewBsdSocketInputStream extends InputStream { 710 | private final int sock; 711 | 712 | public NewBsdSocketInputStream(int s) { 713 | this.sock = s; 714 | } 715 | 716 | public synchronized int read() throws IOException { 717 | return HotSpotVM.readOneByte(this); 718 | } 719 | 720 | public synchronized int read(byte[] bs, int off, int len) throws IOException { 721 | if (len == 0) { 722 | return 0; 723 | } 724 | checkBeforeRead(bs, off, len); 725 | return VirtualMachineImpl.read(sock, bs, off, len); 726 | } 727 | 728 | public void close() throws IOException { 729 | VirtualMachineImpl.close(sock); 730 | } 731 | } 732 | } 733 | 734 | private static class NewWindowsHotSpotVM extends HotSpotVM { 735 | private static final byte[] stub; 736 | 737 | static { 738 | VirtualMachineImpl.init(); 739 | stub = VirtualMachineImpl.generateStub(); 740 | } 741 | 742 | private volatile long proc; 743 | 744 | 745 | public NewWindowsHotSpotVM(int pid) throws Exception { 746 | proc = VirtualMachineImpl.openProcess(pid); 747 | try { 748 | VirtualMachineImpl.enqueue(proc, stub, null, null); 749 | } catch (IOException x) { 750 | throw new Exception(x.getMessage()); 751 | } 752 | } 753 | 754 | public void detach() throws IOException { 755 | if (proc != -1) { 756 | VirtualMachineImpl.closeProcess(proc); 757 | proc = -1; 758 | } 759 | } 760 | 761 | public void execute(String cmd, Object... args) throws Exception { 762 | Random rnd = new Random(); 763 | int r = rnd.nextInt(); 764 | String pipePrefix = "\\\\.\\pipe\\javatool"; 765 | String pipename = pipePrefix + r; 766 | long hPipe; 767 | try { 768 | hPipe = VirtualMachineImpl.createPipe(pipename); 769 | } catch (IOException ce) { 770 | r = rnd.nextInt(); 771 | pipename = pipePrefix + r; 772 | hPipe = VirtualMachineImpl.createPipe(pipename); 773 | } 774 | if (proc == -1) { 775 | VirtualMachineImpl.closePipe(hPipe); 776 | throw new IOException("Detached from target VM"); 777 | } 778 | 779 | try { 780 | VirtualMachineImpl.enqueue(proc, stub, cmd, pipename, args); 781 | VirtualMachineImpl.connectPipe(hPipe); 782 | NewWindowsPipedInputStream in = new NewWindowsPipedInputStream(hPipe); 783 | HotSpotVM.checkExecReturn(null, cmd, in); 784 | HotSpotVM.readRemaining(in); 785 | } catch (IOException ioe) { 786 | VirtualMachineImpl.closePipe(hPipe); 787 | throw ioe; 788 | } 789 | } 790 | 791 | private static class NewWindowsPipedInputStream extends InputStream { 792 | private long pipe; 793 | 794 | public NewWindowsPipedInputStream(long hPipe) { 795 | this.pipe = hPipe; 796 | } 797 | 798 | public synchronized int read() throws IOException { 799 | return HotSpotVM.readOneByte(this); 800 | } 801 | 802 | public synchronized int read(byte[] bs, int off, int len) throws IOException { 803 | if (len == 0) { 804 | return 0; 805 | } 806 | checkBeforeRead(bs, off, len); 807 | return VirtualMachineImpl.readPipe(pipe, bs, off, len); 808 | } 809 | 810 | public void close() throws IOException { 811 | if (pipe != -1) { 812 | VirtualMachineImpl.closePipe(pipe); 813 | pipe = -1; 814 | } 815 | } 816 | } 817 | } 818 | } 819 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/com/aliyun/atp/tool/JcmdCommand.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * Copyright (c) 2022 Alibaba Cloud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | package com.aliyun.atp.tool; 24 | 25 | class JcmdCommand extends Command { 26 | private static final String VM_OPERATION_JCMD = "jcmd"; 27 | private final String jcmdCommand; 28 | 29 | JcmdCommand(String commandName, String jcmdCommand, String description) { 30 | super(commandName, description, new CommandOption[]{}); 31 | this.jcmdCommand = jcmdCommand; 32 | } 33 | 34 | @Override 35 | protected void execute(HotSpotVM vm, String[] args) throws Exception { 36 | String[] p; 37 | p = new String[1 + (args != null ? args.length : 0)]; 38 | p[0] = jcmdCommand; 39 | if (args != null) { 40 | System.arraycopy(args, 0, p, 1, args.length); 41 | } 42 | vm.execute(VM_OPERATION_JCMD, p); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/com/aliyun/atp/tool/Main.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * Copyright (c) 2022, 2023 Alibaba Cloud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | package com.aliyun.atp.tool; 24 | 25 | import java.io.ByteArrayOutputStream; 26 | import java.io.IOException; 27 | import java.io.PrintStream; 28 | import java.util.Iterator; 29 | import java.util.LinkedHashSet; 30 | import java.util.Set; 31 | 32 | public class Main { 33 | private static final Set commands = new LinkedHashSet(); 34 | 35 | static { 36 | registerCommands(); 37 | } 38 | 39 | private static void registerCommands() { 40 | commands.add(new HeapDumpCommand("heap", 41 | "Generate heap dump of Java process")); 42 | commands.add(new ThreadDumpCommand("thread", 43 | "Print all threads and their stack traces")); 44 | commands.add(new HeapHistogramCommand("list_heap", 45 | "List class and number of instance in Java heap")); 46 | commands.add(new FullJcmdCommand("full_jcmd", 47 | "Execute all available jcmd commands and gather their output to a file")); 48 | } 49 | 50 | private static void registerJcmdCommands(HotSpotVM vm) { 51 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 52 | PrintStream ps = new PrintStream(bos); 53 | try { 54 | new JcmdCommand("", "help", "") 55 | .redirectOutput(ps) 56 | .executeCommand(vm, null); 57 | String availableJcmd = bos.toString(); 58 | String[] lines = availableJcmd.split("\n"); 59 | for (int i = 0; i < lines.length; i++) { 60 | if (lines[i].matches("[a-zA-Z_]+?\\.[a-zA-Z_]+")) { 61 | // Read help document of jcmd subcommands 62 | bos = new ByteArrayOutputStream(); 63 | ps = new PrintStream(bos); 64 | new JcmdCommand("", "help " + lines[i], "") 65 | .redirectOutput(ps) 66 | .executeCommand(vm, null); 67 | String jcmdHelp = bos.toString(); 68 | String[] jcmdHelpLines = jcmdHelp.split("\n"); 69 | String description = ""; 70 | if (jcmdHelpLines.length > 2) { 71 | description = jcmdHelpLines[1]; 72 | } 73 | // Register jcmd command accordingly 74 | String cmdName = lines[i]; 75 | cmdName = cmdName.replace(".", "_"); 76 | cmdName = cmdName.toLowerCase(); 77 | commands.add(new JcmdCommand(cmdName, lines[i], description)); 78 | } 79 | } 80 | } catch (Exception e) { 81 | // Failed to execute Jcmd operations 82 | // Skip... 83 | } finally { 84 | try { 85 | bos.close(); 86 | } catch (IOException e) { 87 | throw new RuntimeException(e); 88 | } 89 | } 90 | } 91 | 92 | private static void usage() { 93 | System.err.println("Usage:"); 94 | System.err.println(Main.class.getSimpleName() + " "); 95 | System.err.println(); 96 | System.err.println("Subcommands:"); 97 | Iterator iter = commands.iterator(); 98 | while (iter.hasNext()) { 99 | Command cmd = iter.next(); 100 | System.err.println(cmd.toString()); 101 | } 102 | } 103 | 104 | public static void main(String[] args) { 105 | if (args.length < 1) { 106 | usage(); 107 | return; 108 | } 109 | try { 110 | int pid = Integer.parseInt(args[0]); 111 | HotSpotVM vm = HotSpotVM.creatHotSpotVM(pid); 112 | registerJcmdCommands(vm); 113 | 114 | if (args.length < 2) { 115 | usage(); 116 | return; 117 | } 118 | 119 | String subCommand = args[1]; 120 | Iterator iter = commands.iterator(); 121 | boolean found = false; 122 | while (iter.hasNext()) { 123 | Command cmd = iter.next(); 124 | if (cmd.getName().equals(subCommand)) { 125 | found = true; 126 | cmd.executeCommand(vm, args); 127 | break; 128 | } 129 | } 130 | if (!found) { 131 | throw new Exception("Unknown subcommand " + subCommand); 132 | } 133 | vm.detach(); 134 | } catch (Exception e) { 135 | e.printStackTrace(); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/com/aliyun/atp/tool/ThreadDumpCommand.java: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * Copyright (c) 2022 Alibaba Cloud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | package com.aliyun.atp.tool; 24 | 25 | class ThreadDumpCommand extends Command { 26 | private static final String VM_OPERATION_THREAD_DUMP = "threaddump"; 27 | 28 | ThreadDumpCommand(String commandName, String description) { 29 | super(commandName, description, new CommandOption[]{ 30 | new CommandOption("-lock", "", false, null), 31 | new CommandOption("-extend", "", false, null), 32 | }); 33 | } 34 | 35 | @Override 36 | protected void execute(HotSpotVM vm, String[] args) throws Exception { 37 | boolean printLock = getOption("-lock") != null; 38 | boolean printExtend = getOption("-extend") != null; 39 | String dumpOption = ""; 40 | if (printLock) { 41 | dumpOption += "-l "; 42 | } 43 | if (printExtend) { 44 | dumpOption += "-e "; 45 | } 46 | 47 | vm.execute(VM_OPERATION_THREAD_DUMP, dumpOption.trim()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/sun/tools/attach/BsdVirtualMachine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package sun.tools.attach; 26 | 27 | import java.io.IOException; 28 | 29 | // JNI methods reflection 30 | public class BsdVirtualMachine { 31 | public static native void sendQuitTo(int pid) throws IOException; 32 | 33 | public static native void checkPermissions(String path) throws IOException; 34 | 35 | public static native int socket() throws IOException; 36 | 37 | public static native void connect(int fd, String path) throws IOException; 38 | 39 | public static native void close(int fd) throws IOException; 40 | 41 | public static native int read(int fd, byte[] buf, int off, int bufLen) throws IOException; 42 | 43 | public static native void write(int fd, byte[] buf, int off, int bufLen) throws IOException; 44 | 45 | public static native void createAttachFile(String path); 46 | 47 | public static native String getTempDir(); 48 | } 49 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/sun/tools/attach/LinuxVirtualMachine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package sun.tools.attach; 26 | 27 | import java.io.IOException; 28 | 29 | // JNI methods reflection 30 | public class LinuxVirtualMachine { 31 | public static native void sendQuitTo(int pid) throws IOException; 32 | 33 | public static native void checkPermissions(String path) throws IOException; 34 | 35 | public static native int socket() throws IOException; 36 | 37 | public static native void connect(int fd, String path) throws IOException; 38 | 39 | public static native void close(int fd) throws IOException; 40 | 41 | public static native int read(int fd, byte[] buf, int off, int bufLen) throws IOException; 42 | 43 | public static native void write(int fd, byte[] buf, int off, int bufLen) throws IOException; 44 | } 45 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/sun/tools/attach/VirtualMachineImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package sun.tools.attach; 26 | 27 | import java.io.IOException; 28 | 29 | // JNI methods reflection 30 | public class VirtualMachineImpl { 31 | public static native void sendQuitTo(int pid) throws IOException; 32 | 33 | public static native void checkPermissions(String path) throws IOException; 34 | 35 | public static native int socket() throws IOException; 36 | 37 | public static native void connect(int fd, String path) throws IOException; 38 | 39 | public static native void close(int fd) throws IOException; 40 | 41 | public static native int read(int fd, byte[] buf, int off, int bufLen) throws IOException; 42 | 43 | public static native void write(int fd, byte[] buf, int off, int bufLen) throws IOException; 44 | 45 | public static native void createAttachFile0(String path); 46 | 47 | public static native String getTempDir(); 48 | 49 | public static native void init(); 50 | 51 | public static native byte[] generateStub(); 52 | 53 | public static native long openProcess(int pid) throws IOException; 54 | 55 | public static native void closeProcess(long hProcess) throws IOException; 56 | 57 | public static native long createPipe(String name) throws IOException; 58 | 59 | public static native void closePipe(long hPipe) throws IOException; 60 | 61 | public static native void connectPipe(long hPipe) throws IOException; 62 | 63 | public static native int readPipe(long hPipe, byte[] buf, int off, int buflen) throws IOException; 64 | 65 | public static native void enqueue(long hProcess, byte[] stub, 66 | String cmd, String pipename, Object... args) throws IOException; 67 | 68 | public static int connectToVM(String socketPath) throws IOException { 69 | int fd = socket(); 70 | try { 71 | connect(fd, socketPath); 72 | } catch (IOException x) { 73 | close(fd); 74 | throw x; 75 | } 76 | return fd; 77 | } 78 | 79 | public static void checkConnection(String socketPath) throws IOException { 80 | int fd = socket(); 81 | try { 82 | connect(fd, socketPath); 83 | } finally { 84 | close(fd); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /atp-client-tool/src/main/java/sun/tools/attach/WindowsVirtualMachine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package sun.tools.attach; 26 | 27 | import java.io.IOException; 28 | 29 | // JNI methods reflection 30 | public class WindowsVirtualMachine { 31 | public static native void init(); 32 | 33 | public static native byte[] generateStub(); 34 | 35 | public static native long openProcess(int pid) throws IOException; 36 | 37 | public static native void closeProcess(long hProcess) throws IOException; 38 | 39 | public static native long createPipe(String name) throws IOException; 40 | 41 | public static native void closePipe(long hPipe) throws IOException; 42 | 43 | public static native void connectPipe(long hPipe) throws IOException; 44 | 45 | public static native int readPipe(long hPipe, byte[] buf, int off, int buflen) throws IOException; 46 | 47 | public static native void enqueue(long hProcess, byte[] stub, 48 | String cmd, String pipename, Object... args) throws IOException; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | atp-sdk 8 | https://www.aliyun.com/product/developerservices/atp 9 | Aliyun Application Troubleshooting Platform SDK 10 | 11 | com.aliyun.atp 12 | atp-sdk 13 | 1.0.4 14 | pom 15 | 16 | 17 | atp-client-tool 18 | atp-client-api 19 | 20 | 21 | 22 | 23 | MIT License 24 | https://mit-license.org/ 25 | 26 | 27 | 28 | 29 | https://github.com/aliyun/aliyun-atp-sdk.git 30 | scm:https://github.com/aliyun/aliyun-atp-sdk.git 31 | 32 | 33 | 34 | 35 | y1yang0 36 | Yi Yang 37 | qingfeng.yy@alibaba-inc.com 38 | 39 | 40 | 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-source-plugin 46 | 3.1.0 47 | 48 | 49 | attach-sources 50 | 51 | jar-no-fork 52 | 53 | 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-compiler-plugin 59 | 3.3 60 | 61 | 1.8 62 | 1.8 63 | true 64 | 65 | 66 | 67 | org.apache.maven.plugins 68 | maven-javadoc-plugin 69 | 3.1.0 70 | 71 | 72 | attach-javadocs 73 | 74 | jar 75 | 76 | 77 | 78 | 79 | 8 80 | 81 | 82 | 83 | org.apache.maven.plugins 84 | maven-gpg-plugin 85 | 1.6 86 | 87 | 88 | sign-artifacts 89 | verify 90 | 91 | sign 92 | 93 | 94 | 95 | --batch 96 | --pinentry-mode 97 | loopback 98 | 99 | 100 | ${project.basedir}/../atp-client-api/src/main/resources 101 | 102 | 103 | 104 | 105 | 106 | 107 | org.sonatype.plugins 108 | nexus-staging-maven-plugin 109 | 1.6.7 110 | true 111 | 112 | ossrh 113 | https://oss.sonatype.org/ 114 | true 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-release-plugin 120 | 2.4.2 121 | 122 | 123 | 124 | 125 | 126 | 127 | ossrh 128 | https://oss.sonatype.org/content/repositories/snapshots 129 | 130 | 131 | ossrh 132 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 133 | 134 | 135 | 136 | 137 | --------------------------------------------------------------------------------