Skip to content

Commit 3e0b081

Browse files
kpamnanynickrobinson251
authored andcommitted
RAI: Write heartbeat thread output to safe crash log
1 parent a4bd17f commit 3e0b081

File tree

1 file changed

+58
-28
lines changed

1 file changed

+58
-28
lines changed

src/jl_uv.c

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -813,56 +813,83 @@ JL_DLLEXPORT int jl_printf(uv_stream_t *s, const char *format, ...)
813813
return c;
814814
}
815815

816-
STATIC_INLINE void print_error_msg_as_json(char *buf) JL_NOTSAFEPOINT
816+
STATIC_INLINE int copystp(char *dest, const char *src)
817817
{
818-
// Our telemetry on SPCS expects a JSON object per line
819-
// The following lines prepare the timestamp string and the JSON object
818+
char *d = stpcpy(dest, src);
819+
return (int)(d - dest);
820+
}
821+
822+
// RAI-specific
823+
STATIC_INLINE void write_to_safe_crash_log(char *buf) JL_NOTSAFEPOINT
824+
{
825+
int buflen = strlen(buf);
826+
// Our telemetry on SPCS expects a JSON object per line.
827+
// We ignore write failures because there is nothing we can do.
828+
// We'll use a 2K byte buffer: 69 bytes for JSON message decorations,
829+
// 1 byte for the terminating NUL character, and 3 bytes for an
830+
// ellipsis if we have to truncate the message leaves `max_b` bytes
831+
// for the message.
832+
const int wbuflen = 2048;
833+
const int max_b = wbuflen - 70 - 3;
834+
char wbuf[wbuflen];
835+
bzero(wbuf, wbuflen);
836+
int wlen = 0;
837+
838+
// JSON preamble (32 bytes)
839+
wlen += copystp(&wbuf[wlen], "\n{\"level\":\"Error\", \"timestamp\":\"");
840+
841+
// Timestamp (19 bytes)
820842
struct timeval tv;
821843
struct tm* tm_info;
822-
char timestamp_buffer[50];
823-
// Get current time
824844
gettimeofday(&tv, NULL);
825845
tm_info = gmtime(&tv.tv_sec);
826-
// Format time
827-
int offset = strftime(timestamp_buffer, 25, "%Y-%m-%dT%H:%M:%S", tm_info);
828-
// Append milliseconds
829-
snprintf(timestamp_buffer + offset, 25, ".%03d", tv.tv_usec / 1000);
830-
const char *json_preamble_p1 = "\n{\"level\":\"Error\", \"timestamp\":\"";
831-
const char *json_preamble_p2 = "\", \"message\": \"";
832-
const char *json_postamble = "\"}\n";
833-
// Ignore write failures because there is nothing we can do
834-
write(jl_sig_fd, json_preamble_p1, strlen(json_preamble_p1));
835-
write(jl_sig_fd, timestamp_buffer, strlen(timestamp_buffer));
836-
write(jl_sig_fd, json_preamble_p2, strlen(json_preamble_p2));
837-
// JSON escape the input string
838-
for(size_t i = 0; i < strlen(buf); i += 1) {
846+
wlen += strftime(&wbuf[wlen], 42, "%Y-%m-%dT%H:%M:%S", tm_info);
847+
sprintf(&wbuf[wlen], ".%03ld", (long)tv.tv_usec / 1000);
848+
wlen += 4;
849+
850+
// JSON preamble to message (15 bytes)
851+
wlen += copystp(&wbuf[wlen], "\", \"message\": \"");
852+
853+
// Message
854+
// Each iteration will advance wlen by 1 or 2
855+
for (size_t i = 0; i < buflen; i++) {
856+
// Truncate the message if the write buffer is full
857+
if (wlen == max_b || wlen == max_b - 1) {
858+
wlen += copystp(&wbuf[wlen], "...");
859+
break;
860+
}
839861
switch (buf[i]) {
840862
case '"':
841-
write(jl_sig_fd, "\\\"", 2);
863+
wlen += copystp(&wbuf[wlen], "\\\"");
842864
break;
843865
case '\b':
844-
write(jl_sig_fd, "\\b", 2);
866+
wlen += copystp(&wbuf[wlen], "\\b");
845867
break;
846868
case '\n':
847-
write(jl_sig_fd, "\\n", 2);
869+
wlen += copystp(&wbuf[wlen], "\\n");
848870
break;
849871
case '\r':
850-
write(jl_sig_fd, "\\r", 2);
872+
wlen += copystp(&wbuf[wlen], "\\r");
851873
break;
852874
case '\t':
853-
write(jl_sig_fd, "\\t", 2);
875+
wlen += copystp(&wbuf[wlen], "\\t");
854876
break;
855877
case '\\':
856-
write(jl_sig_fd, "\\\\", 2);
878+
wlen += copystp(&wbuf[wlen], "\\\\");
857879
break;
858880
default:
859-
write(jl_sig_fd, buf + i, 1);
881+
wbuf[wlen++] = buf[i];
882+
break;
860883
}
861884
}
862-
write(jl_sig_fd, json_postamble, strlen(json_postamble));
885+
// JSON completion (3 bytes)
886+
wlen += copystp(&wbuf[wlen], "\"}\n");
887+
write(jl_sig_fd, wbuf, wlen);
863888
fdatasync(jl_sig_fd);
864889
}
865890

891+
extern int jl_inside_heartbeat_thread(void);
892+
866893
JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...)
867894
{
868895
static char buf[1000];
@@ -879,8 +906,11 @@ JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...)
879906
va_end(args);
880907

881908
buf[999] = '\0';
882-
if (jl_inside_signal_handler() && jl_sig_fd != 0) {
883-
print_error_msg_as_json(buf);
909+
// order is important here: we want to ensure that the threading infra
910+
// has been initialized before we start trying to print to the
911+
// safe crash log file
912+
if (jl_sig_fd != 0 && (jl_inside_signal_handler() || jl_inside_heartbeat_thread())) {
913+
write_to_safe_crash_log(buf);
884914
}
885915
if (write(STDERR_FILENO, buf, strlen(buf)) < 0) {
886916
// nothing we can do; ignore the failure

0 commit comments

Comments
 (0)