[Git][ghc/ghc][wip/andreask/escape_json] Profiling: Properly escape characters when using `-pj`.

Andreas Klebinger (@AndreasK) gitlab at gitlab.haskell.org
Mon Sep 11 13:42:07 UTC 2023



Andreas Klebinger pushed to branch wip/andreask/escape_json at Glasgow Haskell Compiler / GHC


Commits:
c25a5b39 by Andreas Klebinger at 2023-09-11T15:41:36+02:00
Profiling: Properly escape characters when using `-pj`.

There are some ways in which unusual characters like quotes or others
can make it into cost centre names. So properly escape these.

Fixes #23924

- - - - -


1 changed file:

- rts/ProfilerReportJson.c


Changes:

=====================================
rts/ProfilerReportJson.c
=====================================
@@ -17,36 +17,178 @@
 
 #include <string.h>
 
-// I don't think this code is all that perf critical.
-// So we just allocate a new buffer each time around.
+// Including zero byte
+static size_t escaped_size(char const* str)
+{
+    size_t escaped_size = 0;
+    for (; *str != '\0'; str++) {
+        const unsigned char c = *str;
+        switch (c)
+            {
+                // quotation mark (0x22)
+                case '"':
+                {
+                    escaped_size += 2;
+                    break;
+                }
+
+                case '\\':
+                {
+                    escaped_size += 2;
+                    break;
+                }
+
+                // backspace (0x08)
+                case '\b':
+                {
+                    escaped_size += 2;
+                    break;
+                }
+
+                // formfeed (0x0c)
+                case '\f':
+                {
+                    escaped_size += 2;
+                    break;
+                }
+
+                // newline (0x0a)
+                case '\n':
+                {
+                    escaped_size += 2;
+                    break;
+                }
+
+                // carriage return (0x0d)
+                case '\r':
+                {
+                    escaped_size += 2;
+                    break;
+                }
+
+                // horizontal tab (0x09)
+                case '\t':
+                {
+                    escaped_size += 2;
+                    break;
+                }
+
+                default:
+                {
+                    if (c <= 0x1f)
+                    {
+                        // print character c as \uxxxx
+                        escaped_size += 6;
+                    }
+                    else
+                    {
+                        escaped_size ++;
+                    }
+                    break;
+                }
+            }
+    }
+    escaped_size++; // null byte
+
+    return escaped_size;
+}
+
 static void escapeString(char const* str, char **buf)
 {
     char *out;
-    size_t req_size; //Max required size for decoding.
-    size_t in_size;  //Input size, including zero.
-
-    in_size = strlen(str) + 1;
-    // The strings are generally small and short
-    // lived so should be ok to just double the size.
-    req_size = in_size * 2;
-    out = stgMallocBytes(req_size, "writeCCSReportJson");
-    *buf = out;
-    // We provide an outputbuffer twice the size of the input,
-    // and at worse double the output size. So we can skip
-    // length checks.
+    size_t out_size; //Max required size for decoding.
+    size_t pos = 0;
+
+    out_size = escaped_size(str); //includes trailing zero byte
+    out = stgMallocBytes(out_size, "writeCCSReportJson");
     for (; *str != '\0'; str++) {
-        char c = *str;
-        if (c == '\\') {
-            *out = '\\'; out++;
-            *out = '\\'; out++;
-        } else if (c == '\n') {
-            *out = '\\'; out++;
-            *out = 'n';  out++;
-        } else {
-            *out = c; out++;
-        }
+        const unsigned char c = *str;
+        switch (c)
+            {
+                // quotation mark (0x22)
+                case '"':
+                {
+                    out[pos] = '\\';
+                    out[pos + 1] = '"';
+                    pos += 2;
+                    break;
+                }
+
+                // reverse solidus (0x5c)
+                case '\\':
+                {
+                    out[pos] = '\\';
+                    out[pos+1] = '\\';
+                    pos += 2;
+                    break;
+                }
+
+                // backspace (0x08)
+                case '\b':
+                {
+                    out[pos] = '\\';
+                    out[pos + 1] = 'b';
+                    pos += 2;
+                    break;
+                }
+
+                // formfeed (0x0c)
+                case '\f':
+                {
+                    out[pos] = '\\';
+                    out[pos + 1] = 'f';
+                    pos += 2;
+                    break;
+                }
+
+                // newline (0x0a)
+                case '\n':
+                {
+                    out[pos] = '\\';
+                    out[pos + 1] = 'n';
+                    pos += 2;
+                    break;
+                }
+
+                // carriage return (0x0d)
+                case '\r':
+                {
+                    out[pos] = '\\';
+                    out[pos + 1] = 'r';
+                    pos += 2;
+                    break;
+                }
+
+                // horizontal tab (0x09)
+                case '\t':
+                {
+                    out[pos] = '\\';
+                    out[pos + 1] = 't';
+                    pos += 2;
+                    break;
+                }
+
+                default:
+                {
+                    if (c <= 0x1f)
+                    {
+                        // print character c as \uxxxx
+                        out[pos] = '\\';
+                        sprintf(&out[pos + 1], "u%04x", (int)c);
+                        pos += 6;
+                    }
+                    else
+                    {
+                        // all other characters are added as-is
+                        out[pos++] = c;
+                    }
+                    break;
+                }
+            }
     }
-    *out = '\0';
+    out[pos++] = '\0';
+    assert(pos == out_size);
+    *buf = out;
 }
 
 static void



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/c25a5b399f773789cefe2275581a24dfd977d36b

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/c25a5b399f773789cefe2275581a24dfd977d36b
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20230911/258ed308/attachment-0001.html>


More information about the ghc-commits mailing list