8254723: add diagnostic command to write Linux perf map file

Reviewed-by: ysuenaga, sspitsyn
This commit is contained in:
Nick Gasson 2020-11-03 01:37:57 +00:00
parent f97ec359ec
commit 50357d136a
8 changed files with 170 additions and 2 deletions

View File

@ -79,7 +79,10 @@
"be dumped into the corefile.") \
\
product(bool, UseCpuAllocPath, false, DIAGNOSTIC, \
"Use CPU_ALLOC code path in os::active_processor_count ")
"Use CPU_ALLOC code path in os::active_processor_count ") \
\
product(bool, DumpPerfMapAtExit, false, DIAGNOSTIC, \
"Write map file for Linux perf tool at exit")
// end of RUNTIME_OS_FLAGS

View File

@ -4635,6 +4635,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;
}

View File

@ -1559,6 +1559,34 @@ void CodeCache::log_state(outputStream* st) {
unallocated_capacity());
}
#ifdef LINUX
void CodeCache::write_perf_map() {
MutexLocker 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;
}
AllCodeBlobsIterator iter(AllCodeBlobsIterator::only_alive_and_not_unloading);
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) {

View File

@ -191,6 +191,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);
@ -409,7 +410,13 @@ struct NMethodFilter {
static const GrowableArray<CodeHeap*>* heaps() { return CodeCache::nmethod_heaps(); }
};
struct AllCodeBlobsFilter {
static bool apply(CodeBlob* cb) { return true; }
static const GrowableArray<CodeHeap*>* heaps() { return CodeCache::heaps(); }
};
typedef CodeBlobIterator<CompiledMethod, CompiledMethodFilter> CompiledMethodIterator;
typedef CodeBlobIterator<nmethod, NMethodFilter> NMethodIterator;
typedef CodeBlobIterator<CodeBlob, AllCodeBlobsFilter> AllCodeBlobsIterator;
#endif // SHARE_CODE_CODECACHE_HPP

View File

@ -478,6 +478,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);
}

View File

@ -110,6 +110,9 @@ void DCmdRegistrant::register_dcmds(){
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompileQueueDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeListDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeCacheDCmd>(full_export, true, false));
#ifdef LINUX
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));
@ -893,6 +896,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),

View File

@ -577,6 +577,29 @@ 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
class CodeListDCmd : public DCmd {
public:
CodeListDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
@ -598,7 +621,6 @@ public:
virtual void execute(DCmdSource source, TRAPS);
};
class CodeCacheDCmd : public DCmd {
public:
CodeCacheDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}

View File

@ -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());
}
}