diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp
index 36656515942..4a54fd8d0a9 100644
--- a/src/hotspot/share/code/codeCache.cpp
+++ b/src/hotspot/share/code/codeCache.cpp
@@ -1788,7 +1788,7 @@ void CodeCache::log_state(outputStream* st) {
 }
 
 #ifdef LINUX
-void CodeCache::write_perf_map(const char* filename) {
+void CodeCache::write_perf_map(const char* filename, outputStream* st) {
   MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
 
   // Perf expects to find the map file at /tmp/perf-<pid>.map
@@ -1801,7 +1801,7 @@ void CodeCache::write_perf_map(const char* filename) {
 
   fileStream fs(filename, "w");
   if (!fs.is_open()) {
-    log_warning(codecache)("Failed to create %s for perf map", filename);
+    st->print_cr("Warning: Failed to create %s for perf map", filename);
     return;
   }
 
diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp
index 8fcbf32b644..565f600453e 100644
--- a/src/hotspot/share/code/codeCache.hpp
+++ b/src/hotspot/share/code/codeCache.hpp
@@ -223,7 +223,7 @@ class CodeCache : AllStatic {
   static void print_trace(const char* event, CodeBlob* cb, uint 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(const char* filename = nullptr);)
+  LINUX_ONLY(static void write_perf_map(const char* filename, outputStream* st);) // Prints warnings and error messages to outputStream
   static const char* get_code_heap_name(CodeBlobType code_blob_type)  { return (heap_available(code_blob_type) ? get_code_heap(code_blob_type)->name() : "Unused"); }
   static void report_codemem_full(CodeBlobType code_blob_type, bool print);
 
diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp
index 7ffe56d9715..23df464aba8 100644
--- a/src/hotspot/share/runtime/java.cpp
+++ b/src/hotspot/share/runtime/java.cpp
@@ -484,7 +484,7 @@ void before_exit(JavaThread* thread, bool halt) {
 
 #ifdef LINUX
   if (DumpPerfMapAtExit) {
-    CodeCache::write_perf_map();
+    CodeCache::write_perf_map(nullptr, tty);
   }
   if (PrintMemoryMapAtExit) {
     MemMapPrinter::print_all_mappings(tty);
diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp
index 7d2325c16b6..e6328e95abd 100644
--- a/src/hotspot/share/services/diagnosticCommand.cpp
+++ b/src/hotspot/share/services/diagnosticCommand.cpp
@@ -865,7 +865,7 @@ void PerfMapDCmd::execute(DCmdSource source, TRAPS) {
   // The check for _filename.is_set() is because we don't want to use
   // DEFAULT_PERFMAP_FILENAME, since it is meant as a description
   // of the default, not the actual default.
-  CodeCache::write_perf_map(_filename.is_set() ? _filename.value() : nullptr);
+  CodeCache::write_perf_map(_filename.is_set() ? _filename.value() : nullptr, output());
 }
 #endif // LINUX
 
diff --git a/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java b/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java
index 6bbe62044d0..7a72efd68ba 100644
--- a/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java
+++ b/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2020, 2023, Arm Limited. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -113,4 +113,16 @@ public class PerfMapTest {
         run(new JMXExecutor(), "Compiler.perfmap " + path.toString(), path);
         Files.deleteIfExists(path);
     }
+
+    @Test
+    public void logErrorsDcmdOutputStream() throws IOException {
+        String test_dir = System.getProperty("test.dir", ".");
+        Path path = Paths.get("nonexistent", test_dir);
+        try {
+            OutputAnalyzer output = new JMXExecutor().execute("Compiler.perfmap %s".formatted(path));
+            output.shouldContain("Warning: Failed to create nonexistent/%s for perf map".formatted(test_dir));
+        } finally {
+            Files.deleteIfExists(path);
+        }
+    }
 }