@@ -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+
866893JL_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