diff --git a/node.gyp b/node.gyp
index 91a6304049931a..8dc9187c5af2e8 100644
--- a/node.gyp
+++ b/node.gyp
@@ -981,6 +981,7 @@
         'test/cctest/test_node_postmortem_metadata.cc',
         'test/cctest/test_environment.cc',
         'test/cctest/test_platform.cc',
+        'test/cctest/test_report_util.cc',
         'test/cctest/test_traced_value.cc',
         'test/cctest/test_util.cc',
         'test/cctest/test_url.cc'
diff --git a/src/node_report_utils.cc b/src/node_report_utils.cc
index e93f230d318a6d..4c3a6d6ba61ffe 100644
--- a/src/node_report_utils.cc
+++ b/src/node_report_utils.cc
@@ -271,28 +271,41 @@ void WalkHandle(uv_handle_t* h, void* arg) {
   writer->json_end();
 }
 
-static std::string findAndReplace(const std::string& str,
-                                  const std::string& old,
-                                  const std::string& neu) {
-  std::string ret = str;
+std::string EscapeJsonChars(const std::string& str) {
+  const std::string control_symbols[0x20] = {
+      "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005",
+      "\\u0006", "\\u0007", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r",
+      "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013",
+      "\\u0014", "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019",
+      "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f"
+  };
+
+  std::string ret = "";
+  size_t last_pos = 0;
   size_t pos = 0;
-  while ((pos = ret.find(old, pos)) != std::string::npos) {
-    ret.replace(pos, old.length(), neu);
-    pos += neu.length();
+  for (; pos < str.size(); ++pos) {
+    std::string replace;
+    char ch = str[pos];
+    if (ch == '\\') {
+      replace = "\\\\";
+    } else if (ch == '\"') {
+      replace = "\\\"";
+    } else {
+      size_t num = static_cast<size_t>(ch);
+      if (num < 0x20) replace = control_symbols[num];
+    }
+    if (!replace.empty()) {
+      if (pos > last_pos) {
+        ret += str.substr(last_pos, pos - last_pos);
+      }
+      last_pos = pos + 1;
+      ret += replace;
+    }
+  }
+  // Append any remaining symbols.
+  if (last_pos < str.size()) {
+    ret += str.substr(last_pos, pos - last_pos);
   }
-  return ret;
-}
-
-std::string EscapeJsonChars(const std::string& str) {
-  std::string ret = str;
-  ret = findAndReplace(ret, "\\", "\\\\");
-  ret = findAndReplace(ret, "\\u", "\\u");
-  ret = findAndReplace(ret, "\n", "\\n");
-  ret = findAndReplace(ret, "\f", "\\f");
-  ret = findAndReplace(ret, "\r", "\\r");
-  ret = findAndReplace(ret, "\b", "\\b");
-  ret = findAndReplace(ret, "\t", "\\t");
-  ret = findAndReplace(ret, "\"", "\\\"");
   return ret;
 }
 
diff --git a/test/cctest/test_report_util.cc b/test/cctest/test_report_util.cc
new file mode 100644
index 00000000000000..e32558ef75b5e8
--- /dev/null
+++ b/test/cctest/test_report_util.cc
@@ -0,0 +1,26 @@
+#include "node_report.h"
+
+#include "gtest/gtest.h"
+
+TEST(ReportUtilTest, EscapeJsonChars) {
+  using report::EscapeJsonChars;
+  EXPECT_EQ("abc", EscapeJsonChars("abc"));
+  EXPECT_EQ("abc\\n", EscapeJsonChars("abc\n"));
+  EXPECT_EQ("abc\\nabc", EscapeJsonChars("abc\nabc"));
+  EXPECT_EQ("abc\\\\", EscapeJsonChars("abc\\"));
+  EXPECT_EQ("abc\\\"", EscapeJsonChars("abc\""));
+
+  const std::string expected[0x20] = {
+      "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005",
+      "\\u0006", "\\u0007", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r",
+      "\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013",
+      "\\u0014", "\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019",
+      "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f"
+  };
+  for (int i = 0; i < 0x20; ++i) {
+    char symbols[1] = { static_cast<char>(i) };
+    std::string input(symbols, 1);
+    EXPECT_EQ(expected[i], EscapeJsonChars(input));
+    EXPECT_EQ("a" + expected[i], EscapeJsonChars("a" + input));
+  }
+}