Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8254723: add diagnostic command to write Linux perf map file #760

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/hotspot/os/linux/globals_linux.hpp
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions src/hotspot/os/linux/os_linux.cpp
Expand Up @@ -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;
}

Expand Down
28 changes: 28 additions & 0 deletions src/hotspot/share/code/codeCache.cpp
Expand Up @@ -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());
nick-arm marked this conversation as resolved.
Show resolved Hide resolved

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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation isn't right.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean how the arguments are lined up on the continuation line below?

(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
7 changes: 7 additions & 0 deletions src/hotspot/share/code/codeCache.hpp
Expand Up @@ -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);

Expand Down Expand Up @@ -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
6 changes: 6 additions & 0 deletions src/hotspot/share/runtime/java.cpp
Expand Up @@ -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);
}
Expand Down
9 changes: 9 additions & 0 deletions src/hotspot/share/services/diagnosticCommand.cpp
Expand Up @@ -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));

Expand Down Expand Up @@ -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),
Expand Down
24 changes: 23 additions & 1 deletion src/hotspot/share/services/diagnosticCommand.hpp
Expand Up @@ -577,6 +577,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() {
nick-arm marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -598,7 +621,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
@@ -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());
}
}