From 6a5759c82b869c4d931273609aa19eb1a84df8db Mon Sep 17 00:00:00 2001
Date: Thu, 21 Sep 2023 15:12:51 +0800
Subject: 8254723-Add-diagnostic-command-to-write-Linux-perf-m.patch

---
 hotspot/src/os/linux/vm/globals_linux.hpp     |  5 +-
 hotspot/src/os/linux/vm/os_linux.cpp          |  6 ++
 hotspot/src/share/vm/code/codeCache.cpp       | 36 ++++++++
 hotspot/src/share/vm/code/codeCache.hpp       |  1 +
 hotspot/src/share/vm/runtime/java.cpp         |  6 ++
 .../share/vm/services/diagnosticCommand.cpp   |  7 ++
 .../share/vm/services/diagnosticCommand.hpp   | 23 +++++
 .../test/serviceability/dcmd/PerfMapTest.java | 84 +++++++++++++++++++
 8 files changed, 167 insertions(+), 1 deletion(-)
 create mode 100644 hotspot/test/serviceability/dcmd/PerfMapTest.java

diff --git a/hotspot/src/os/linux/vm/globals_linux.hpp b/hotspot/src/os/linux/vm/globals_linux.hpp
index f98bde41a..5cbe686d3 100644
--- a/hotspot/src/os/linux/vm/globals_linux.hpp
+++ b/hotspot/src/os/linux/vm/globals_linux.hpp
@@ -55,7 +55,10 @@
   product(bool, PreferContainerQuotaForCPUCount, true,                  \
           "Calculate the container CPU availability based on the value" \
           " of quotas (if set), when true. Otherwise, use the CPU"      \
-          " shares value, provided it is less than quota.")
+          " shares value, provided it is less than quota.")             \
+                                                                        \
+  diagnostic(bool, DumpPerfMapAtExit, false,                            \
+          "Write map file for Linux perf tool at exit")
 
 //
 // Defines Linux-specific default values. The flags are available on all
diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp
index a1cc85ca3..197b5c193 100644
--- a/hotspot/src/os/linux/vm/os_linux.cpp
+++ b/hotspot/src/os/linux/vm/os_linux.cpp
@@ -5648,6 +5648,12 @@ jint os::init_2(void)
   // initialize thread priority policy
   prio_init();
 
+  if (DumpPerfMapAtExit && FLAG_IS_DEFAULT(UseCodeCacheFlushing)) {
+    // Disable code cache flushing to ensure the map file written at
+    // exit contains all nmethods generated during execution.
+    FLAG_SET_DEFAULT(UseCodeCacheFlushing, false);
+  }
+
   return JNI_OK;
 }
 
diff --git a/hotspot/src/share/vm/code/codeCache.cpp b/hotspot/src/share/vm/code/codeCache.cpp
index 37f24b5e9..97ad3ba79 100644
--- a/hotspot/src/share/vm/code/codeCache.cpp
+++ b/hotspot/src/share/vm/code/codeCache.cpp
@@ -1033,3 +1033,39 @@ void CodeCache::log_state(outputStream* st) {
             unallocated_capacity());
 }
 
+#ifdef LINUX
+void CodeCache::write_perf_map() {
+  MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+
+  // Perf expects to find the map file at /tmp/perf-<pid>.map.
+  char fname[32];
+  jio_snprintf(fname, sizeof(fname), "/tmp/perf-%d.map", os::current_process_id());
+
+  fileStream fs(fname, "w");
+  if (!fs.is_open()) {
+    DEBUG_ONLY(warning("[codecache] Failed to create %s for perf map", fname));
+    return;
+  }
+
+  FOR_ALL_ALIVE_BLOBS(cb) {
+    if (cb->is_nmethod()) {
+      nmethod *nm = (nmethod *) cb;
+      assert(!nm->is_unloaded(), "Tautology");
+      ResourceMark rm;
+      const char* method_name = nm->method()->name_and_sig_as_C_string();
+      fs.print_cr(INTPTR_FORMAT " " INTPTR_FORMAT " %s",
+                  (intptr_t)cb->code_begin(), (intptr_t)cb->code_size(),
+                  method_name);
+    }
+    if (cb->is_runtime_stub()) {
+      RuntimeStub *stub = (RuntimeStub *) cb;
+      ResourceMark rm;
+      const char* method_name = stub->name();
+      fs.print_cr(INTPTR_FORMAT " " INTPTR_FORMAT " %s",
+                  (intptr_t)cb->code_begin(), (intptr_t)cb->code_size(),
+                  method_name);
+    }
+  }
+}
+
+#endif // LINUX
diff --git a/hotspot/src/share/vm/code/codeCache.hpp b/hotspot/src/share/vm/code/codeCache.hpp
index ab1417b19..0aad2d648 100644
--- a/hotspot/src/share/vm/code/codeCache.hpp
+++ b/hotspot/src/share/vm/code/codeCache.hpp
@@ -158,6 +158,7 @@ class CodeCache : AllStatic {
   static void print_trace(const char* event, CodeBlob* cb, int size = 0) PRODUCT_RETURN;
   static void print_summary(outputStream* st, bool detailed = true); // Prints a summary of the code cache usage
   static void log_state(outputStream* st);
+  LINUX_ONLY(static void write_perf_map();)
 
   // Dcmd (Diagnostic commands)
   static void print_codelist(outputStream* st);
diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp
index 5a628b73e..fec8fb94d 100644
--- a/hotspot/src/share/vm/runtime/java.cpp
+++ b/hotspot/src/share/vm/runtime/java.cpp
@@ -537,6 +537,12 @@ void before_exit(JavaThread * thread) {
     BytecodeHistogram::print();
   }
 
+#ifdef LINUX
+  if (DumpPerfMapAtExit) {
+    CodeCache::write_perf_map();
+  }
+#endif
+
   if (JvmtiExport::should_post_thread_life()) {
     JvmtiExport::post_thread_end(thread);
   }
diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp
index 416dc77ce..f8f6ad546 100644
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp
@@ -36,6 +36,7 @@
 #include "services/management.hpp"
 #include "utilities/macros.hpp"
 #include "oops/objArrayOop.hpp"
+#include "code/codeCache.hpp"
 
 #ifdef LINUX
 #include "trimCHeapDCmd.hpp"
@@ -81,6 +82,7 @@ void DCmdRegistrant::register_dcmds(){
 #ifdef LINUX
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TrimCLibcHeapDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<MallocInfoDcmd>(full_export, true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PerfMapDCmd>(full_export, true, false));
 #endif // LINUX
 
   // Enhanced JMX Agent Support
@@ -868,3 +870,8 @@ void CodeCacheDCmd::execute(DCmdSource source, TRAPS) {
   VMThread::execute(&printCodeCacheOp);
 }
 
+#ifdef LINUX
+void PerfMapDCmd::execute(DCmdSource source, TRAPS) {
+  CodeCache::write_perf_map();
+}
+#endif // LINUX
\ No newline at end of file
diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp
index 3733fa7f7..d446aab4e 100644
--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp
+++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp
@@ -590,4 +590,27 @@ public:
   virtual void execute(DCmdSource source, TRAPS);
 };
 
+#ifdef LINUX
+class PerfMapDCmd : public DCmd {
+public:
+    PerfMapDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
+    static const char* name() {
+      return "Compiler.perfmap";
+    }
+    static const char* description() {
+      return "Write map file for Linux perf tool.";
+    }
+    static const char* impact() {
+      return "Low";
+    }
+    static const JavaPermission permission() {
+      JavaPermission p = {"java.lang.management.ManagementPermission",
+                          "monitor", NULL};
+      return p;
+    }
+    static int num_arguments() { return 0; }
+    virtual void execute(DCmdSource source, TRAPS);
+};
+#endif // LINUX
+
 #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
diff --git a/hotspot/test/serviceability/dcmd/PerfMapTest.java b/hotspot/test/serviceability/dcmd/PerfMapTest.java
new file mode 100644
index 000000000..1807b2a7f
--- /dev/null
+++ b/hotspot/test/serviceability/dcmd/PerfMapTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test PerfMapTest
+ * @bug 8254723
+ * @requires os.family == "linux"
+ * @library /testlibrary
+ * @run testng/othervm PerfMapTest
+ * @summary Test of diagnostic command Compiler.perfmap
+ */
+
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+import com.oracle.java.testlibrary.OutputAnalyzer;
+import com.oracle.java.testlibrary.CommandExecutor;
+import com.oracle.java.testlibrary.JMXExecutor;
+import com.oracle.java.testlibrary.ProcessTools;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Call jcmd Compiler.perfmap and check the output file has the expected
+ * format.
+ */
+public class PerfMapTest {
+
+    static final Pattern LINE_PATTERN =
+        Pattern.compile("^((?:0x)?\\p{XDigit}+)\\s+((?:0x)?\\p{XDigit}+)\\s+(.*)$");
+
+    public void run(CommandExecutor executor) throws Exception {
+        OutputAnalyzer output = executor.execute("Compiler.perfmap");
+
+        output.stderrShouldBeEmpty();
+        output.stdoutShouldBeEmpty();
+
+        final long pid = ProcessTools.getProcessId();
+        final Path path = Paths.get(String.format("/tmp/perf-%d.map", pid));
+
+        Assert.assertTrue(Files.exists(path));
+
+        // Sanity check the file contents
+        try {
+            for (String entry : Files.readAllLines(path)) {
+                Matcher m = LINE_PATTERN.matcher(entry);
+                Assert.assertTrue(m.matches(), "Invalid file format: " + entry);
+            }
+        } catch (IOException e) {
+            Assert.fail(e.toString());
+        }
+    }
+
+    @Test
+    public void jmx() throws Exception {
+        run(new JMXExecutor());
+    }
+}
-- 
2.22.0

