Skip to content

Commit

Permalink
[Backport] 8254723: add diagnostic command to write Linux perf map file
Browse files Browse the repository at this point in the history
Summary: Support dumping text file format describing the mapping between
address ranges and Java symbol names for Linux perf tool.
There are two ways to trigger dumping:
1. Launch java application with option -XX:+DumpPerfMapAtExit, the file
will be generated automatically when application exits.
2. Use command 'jcmd <pid> Compiler.perfmap' to trigger dump during
application running.

Testing: test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java

Reviewers: kuaiwei.kw, denghui.ddh

Issue: #744
  • Loading branch information
lusou-zhangquan committed Dec 19, 2023
1 parent fbde3c1 commit 4f2b361
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 2 deletions.
6 changes: 5 additions & 1 deletion src/hotspot/os/linux/globals_linux.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@
"be dumped into the corefile.") \
\
diagnostic(bool, UseCpuAllocPath, false, \
"Use CPU_ALLOC code path in os::active_processor_count ")
"Use CPU_ALLOC code path in os::active_processor_count ") \
\
diagnostic(bool, DumpPerfMapAtExit, false, \
"Write map file for Linux perf tool at exit")


//
// Defines Linux-specific default values. The flags are available on all
Expand Down
6 changes: 6 additions & 0 deletions src/hotspot/os/linux/os_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5623,6 +5623,12 @@ jint os::init_2(void) {
set_coredump_filter(FILE_BACKED_SHARED_BIT);
}

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;
}

Expand Down
32 changes: 32 additions & 0 deletions src/hotspot/share/code/codeCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,38 @@ 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()) {
log_warning(codecache)("Failed to create %s for perf map", fname);
return;
}

// In latest hotspot, code cache sweeper supports unloading nmethods concurrently with GC thread.
// One nmethod marked as unloading will be unloaded in the future but still keeps alive at present.
// Here sweeper hasn't supported concurrent unloading yet and no nmethods are in unloading state,
// therefore we only iterate live code blobs.
LiveCodeBlobsIterator iter;
while (iter.next()) {
CodeBlob *cb = iter.method();
ResourceMark rm;
const char* method_name =
cb->is_compiled() ? cb->as_compiled_method()->method()->external_name()
: cb->name();
fs.print_cr(INTPTR_FORMAT " " INTPTR_FORMAT " %s",
(intptr_t)cb->code_begin(), (intptr_t)cb->code_size(),
method_name);
}
}
#endif // LINUX

//---< BEGIN >--- CodeHeap State Analytics.

void CodeCache::aggregate(outputStream *out, size_t granularity) {
Expand Down
6 changes: 6 additions & 0 deletions src/hotspot/share/code/codeCache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,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();)
static const char* get_code_heap_name(int code_blob_type) { return (heap_available(code_blob_type) ? get_code_heap(code_blob_type)->name() : "Unused"); }
static void report_codemem_full(int code_blob_type, bool print);

Expand Down Expand Up @@ -413,8 +414,13 @@ struct NMethodFilter {
static const GrowableArray<CodeHeap*>* heaps() { return CodeCache::nmethod_heaps(); }
};

struct LiveCodeBlobFilter {
static bool apply(CodeBlob* cb) { return cb->is_alive(); }
static const GrowableArray<CodeHeap*>* heaps() { return CodeCache::heaps(); }
};

typedef CodeBlobIterator<CompiledMethod, CompiledMethodFilter> CompiledMethodIterator;
typedef CodeBlobIterator<nmethod, NMethodFilter> NMethodIterator;
typedef CodeBlobIterator<CodeBlob, LiveCodeBlobFilter> LiveCodeBlobsIterator;

#endif // SHARE_VM_CODE_CODECACHE_HPP
6 changes: 6 additions & 0 deletions src/hotspot/share/runtime/java.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,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);
}
Expand Down
7 changes: 7 additions & 0 deletions src/hotspot/share/services/diagnosticCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ void DCmdRegistrant::register_dcmds(){
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeCacheDCmd>(full_export, true, false));
#ifdef LINUX
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TrimCLibcHeapDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PerfMapDCmd>(full_export, true, false));
#endif // LINUX
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<TouchedMethodsDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeHeapAnalyticsDCmd>(full_export, true, false));
Expand Down Expand Up @@ -995,6 +996,12 @@ void CodeCacheDCmd::execute(DCmdSource source, TRAPS) {
CodeCache::print_layout(output());
}

#ifdef LINUX
void PerfMapDCmd::execute(DCmdSource source, TRAPS) {
CodeCache::write_perf_map();
}
#endif // LINUX

//---< BEGIN >--- CodeHeap State Analytics.
CodeHeapAnalyticsDCmd::CodeHeapAnalyticsDCmd(outputStream* output, bool heap) :
DCmdWithParser(output, heap),
Expand Down
24 changes: 23 additions & 1 deletion src/hotspot/share/services/diagnosticCommand.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,29 @@ class CompileQueueDCmd : public DCmd {
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

class CodeListDCmd : public DCmd {
public:
CodeListDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
Expand All @@ -624,7 +647,6 @@ class CodeListDCmd : public DCmd {
virtual void execute(DCmdSource source, TRAPS);
};


class CodeCacheDCmd : public DCmd {
public:
CodeCacheDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
Expand Down
87 changes: 87 additions & 0 deletions test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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 /test/lib
* @modules java.base/jdk.internal.misc
* java.compiler
* java.management
* jdk.internal.jvmstat/sun.jvmstat.monitor
* @run testng/othervm PerfMapTest
* @summary Test of diagnostic command Compiler.perfmap
*/

import org.testng.annotations.Test;
import org.testng.Assert;

import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.dcmd.CommandExecutor;
import jdk.test.lib.dcmd.JMXExecutor;

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) {
OutputAnalyzer output = executor.execute("Compiler.perfmap");

output.stderrShouldBeEmpty();
output.stdoutShouldBeEmpty();

final long pid = ProcessHandle.current().pid();
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() {
run(new JMXExecutor());
}
}

0 comments on commit 4f2b361

Please sign in to comment.