diff --git a/src/node_internals.h b/src/node_internals.h
index 55f5ac40ccc408..abe01e6fed897c 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -95,6 +95,7 @@ void ResetStdio();  // Safe to call more than once and from signal handlers.
 void SignalExit(int signal, siginfo_t* info, void* ucontext);
 #endif
 
+std::string GetProcessTitle(const char* default_title);
 std::string GetHumanReadableProcessName();
 void GetHumanReadableProcessName(char (*name)[1024]);
 
diff --git a/src/node_process_object.cc b/src/node_process_object.cc
index a1bf90c8d69fc0..7cf8c125009606 100644
--- a/src/node_process_object.cc
+++ b/src/node_process_object.cc
@@ -32,11 +32,11 @@ using v8::Value;
 
 static void ProcessTitleGetter(Local<Name> property,
                                const PropertyCallbackInfo<Value>& info) {
-  char buffer[512];
-  uv_get_process_title(buffer, sizeof(buffer));
+  std::string title = GetProcessTitle("node");
   info.GetReturnValue().Set(
-      String::NewFromUtf8(info.GetIsolate(), buffer, NewStringType::kNormal)
-          .ToLocalChecked());
+      String::NewFromUtf8(info.GetIsolate(), title.data(),
+                          NewStringType::kNormal, title.size())
+      .ToLocalChecked());
 }
 
 static void ProcessTitleSetter(Local<Name> property,
diff --git a/src/node_v8_platform-inl.h b/src/node_v8_platform-inl.h
index ecd8f21d1a2653..0f4a98a98551ba 100644
--- a/src/node_v8_platform-inl.h
+++ b/src/node_v8_platform-inl.h
@@ -21,12 +21,12 @@ class NodeTraceStateObserver
     : public v8::TracingController::TraceStateObserver {
  public:
   inline void OnTraceEnabled() override {
-    char name_buffer[512];
-    if (uv_get_process_title(name_buffer, sizeof(name_buffer)) == 0) {
+    std::string title = GetProcessTitle("");
+    if (!title.empty()) {
       // Only emit the metadata event if the title can be retrieved
       // successfully. Ignore it otherwise.
       TRACE_EVENT_METADATA1(
-          "__metadata", "process_name", "name", TRACE_STR_COPY(name_buffer));
+          "__metadata", "process_name", "name", TRACE_STR_COPY(title.c_str()));
     }
     TRACE_EVENT_METADATA1("__metadata",
                           "version",
diff --git a/src/util.cc b/src/util.cc
index 26dbfe844995eb..2a594d81ecab8e 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -22,6 +22,7 @@
 #include "util.h"  // NOLINT(build/include_inline)
 #include "util-inl.h"
 
+#include "debug_utils-inl.h"
 #include "env-inl.h"
 #include "node_buffer.h"
 #include "node_errors.h"
@@ -45,6 +46,7 @@
 
 #include <atomic>
 #include <cstdio>
+#include <cstring>
 #include <iomanip>
 #include <sstream>
 
@@ -133,10 +135,30 @@ void LowMemoryNotification() {
   }
 }
 
+std::string GetProcessTitle(const char* default_title) {
+  std::string buf(16, '\0');
+
+  for (;;) {
+    const int rc = uv_get_process_title(&buf[0], buf.size());
+
+    if (rc == 0)
+      break;
+
+    if (rc != UV_ENOBUFS)
+      return default_title;
+
+    buf.resize(2 * buf.size());
+  }
+
+  // Strip excess trailing nul bytes. Using strlen() here is safe,
+  // uv_get_process_title() always zero-terminates the result.
+  buf.resize(strlen(&buf[0]));
+
+  return buf;
+}
+
 std::string GetHumanReadableProcessName() {
-  char name[1024];
-  GetHumanReadableProcessName(&name);
-  return name;
+  return SPrintF("%s[%d]", GetProcessTitle("Node.js"), uv_os_getpid());
 }
 
 void GetHumanReadableProcessName(char (*name)[1024]) {
diff --git a/test/parallel/test-process-title.js b/test/parallel/test-process-title.js
new file mode 100644
index 00000000000000..dc200df6e1baf6
--- /dev/null
+++ b/test/parallel/test-process-title.js
@@ -0,0 +1,15 @@
+'use strict';
+const common = require('../common');
+const { spawnSync } = require('child_process');
+const { strictEqual } = require('assert');
+
+// FIXME add sunos support
+if (common.isSunOS)
+  common.skip(`Unsupported platform [${process.platform}]`);
+// FIXME add IBMi support
+if (common.isIBMi)
+  common.skip('Unsupported platform IBMi');
+
+const xs = 'x'.repeat(1024);
+const proc = spawnSync(process.execPath, ['-p', 'process.title', xs]);
+strictEqual(proc.stdout.toString().trim(), process.execPath);