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.") \
|
||||
\
|
||||
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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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) {}
|
||||
|
@ -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…
Reference in New Issue
Block a user