8254723: add diagnostic command to write Linux perf map file
Reviewed-by: ysuenaga, sspitsyn
This commit is contained in:
parent
f97ec359ec
commit
50357d136a
@ -79,7 +79,10 @@
|
|||||||
"be dumped into the corefile.") \
|
"be dumped into the corefile.") \
|
||||||
\
|
\
|
||||||
product(bool, UseCpuAllocPath, false, DIAGNOSTIC, \
|
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
|
// end of RUNTIME_OS_FLAGS
|
||||||
|
|
||||||
|
@ -4635,6 +4635,12 @@ jint os::init_2(void) {
|
|||||||
set_coredump_filter(FILE_BACKED_SHARED_BIT);
|
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;
|
return JNI_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1559,6 +1559,34 @@ void CodeCache::log_state(outputStream* st) {
|
|||||||
unallocated_capacity());
|
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.
|
//---< BEGIN >--- CodeHeap State Analytics.
|
||||||
|
|
||||||
void CodeCache::aggregate(outputStream *out, size_t granularity) {
|
void CodeCache::aggregate(outputStream *out, size_t granularity) {
|
||||||
|
@ -191,6 +191,7 @@ class CodeCache : AllStatic {
|
|||||||
static void print_trace(const char* event, CodeBlob* cb, int size = 0) PRODUCT_RETURN;
|
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 print_summary(outputStream* st, bool detailed = true); // Prints a summary of the code cache usage
|
||||||
static void log_state(outputStream* st);
|
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 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);
|
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(); }
|
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<CompiledMethod, CompiledMethodFilter> CompiledMethodIterator;
|
||||||
typedef CodeBlobIterator<nmethod, NMethodFilter> NMethodIterator;
|
typedef CodeBlobIterator<nmethod, NMethodFilter> NMethodIterator;
|
||||||
|
typedef CodeBlobIterator<CodeBlob, AllCodeBlobsFilter> AllCodeBlobsIterator;
|
||||||
|
|
||||||
#endif // SHARE_CODE_CODECACHE_HPP
|
#endif // SHARE_CODE_CODECACHE_HPP
|
||||||
|
@ -478,6 +478,12 @@ void before_exit(JavaThread* thread) {
|
|||||||
BytecodeHistogram::print();
|
BytecodeHistogram::print();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef LINUX
|
||||||
|
if (DumpPerfMapAtExit) {
|
||||||
|
CodeCache::write_perf_map();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (JvmtiExport::should_post_thread_life()) {
|
if (JvmtiExport::should_post_thread_life()) {
|
||||||
JvmtiExport::post_thread_end(thread);
|
JvmtiExport::post_thread_end(thread);
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,9 @@ void DCmdRegistrant::register_dcmds(){
|
|||||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CompileQueueDCmd>(full_export, true, false));
|
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<CodeListDCmd>(full_export, true, false));
|
||||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeCacheDCmd>(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<TouchedMethodsDCmd>(full_export, true, false));
|
||||||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CodeHeapAnalyticsDCmd>(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());
|
CodeCache::print_layout(output());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef LINUX
|
||||||
|
void PerfMapDCmd::execute(DCmdSource source, TRAPS) {
|
||||||
|
CodeCache::write_perf_map();
|
||||||
|
}
|
||||||
|
#endif // LINUX
|
||||||
|
|
||||||
//---< BEGIN >--- CodeHeap State Analytics.
|
//---< BEGIN >--- CodeHeap State Analytics.
|
||||||
CodeHeapAnalyticsDCmd::CodeHeapAnalyticsDCmd(outputStream* output, bool heap) :
|
CodeHeapAnalyticsDCmd::CodeHeapAnalyticsDCmd(outputStream* output, bool heap) :
|
||||||
DCmdWithParser(output, heap),
|
DCmdWithParser(output, heap),
|
||||||
|
@ -577,6 +577,29 @@ public:
|
|||||||
virtual void execute(DCmdSource source, TRAPS);
|
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 {
|
class CodeListDCmd : public DCmd {
|
||||||
public:
|
public:
|
||||||
CodeListDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
|
CodeListDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
|
||||||
@ -598,7 +621,6 @@ public:
|
|||||||
virtual void execute(DCmdSource source, TRAPS);
|
virtual void execute(DCmdSource source, TRAPS);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class CodeCacheDCmd : public DCmd {
|
class CodeCacheDCmd : public DCmd {
|
||||||
public:
|
public:
|
||||||
CodeCacheDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
|
CodeCacheDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user