@@ -814,56 +814,83 @@ JL_DLLEXPORT int jl_printf(uv_stream_t *s, const char *format, ...)
814814 return c ;
815815}
816816
817- STATIC_INLINE void print_error_msg_as_json (char * buf ) JL_NOTSAFEPOINT
817+ STATIC_INLINE int copystp (char * dest , const char * src )
818818{
819- // Our telemetry on SPCS expects a JSON object per line
820- // The following lines prepare the timestamp string and the JSON object
819+ char * d = stpcpy (dest , src );
820+ return (int )(d - dest );
821+ }
822+
823+ // RAI-specific
824+ STATIC_INLINE void write_to_safe_crash_log (char * buf ) JL_NOTSAFEPOINT
825+ {
826+ int buflen = strlen (buf );
827+ // Our telemetry on SPCS expects a JSON object per line.
828+ // We ignore write failures because there is nothing we can do.
829+ // We'll use a 2K byte buffer: 69 bytes for JSON message decorations,
830+ // 1 byte for the terminating NUL character, and 3 bytes for an
831+ // ellipsis if we have to truncate the message leaves `max_b` bytes
832+ // for the message.
833+ const int wbuflen = 2048 ;
834+ const int max_b = wbuflen - 70 - 3 ;
835+ char wbuf [wbuflen ];
836+ bzero (wbuf , wbuflen );
837+ int wlen = 0 ;
838+
839+ // JSON preamble (32 bytes)
840+ wlen += copystp (& wbuf [wlen ], "\n{\"level\":\"Error\", \"timestamp\":\"" );
841+
842+ // Timestamp (19 bytes)
821843 struct timeval tv ;
822844 struct tm * tm_info ;
823- char timestamp_buffer [50 ];
824- // Get current time
825845 gettimeofday (& tv , NULL );
826846 tm_info = gmtime (& tv .tv_sec );
827- // Format time
828- int offset = strftime (timestamp_buffer , 25 , "%Y-%m-%dT%H:%M:%S" , tm_info );
829- // Append milliseconds
830- snprintf (timestamp_buffer + offset , 25 , ".%03d" , tv .tv_usec / 1000 );
831- const char * json_preamble_p1 = "\n{\"level\":\"Error\", \"timestamp\":\"" ;
832- const char * json_preamble_p2 = "\", \"message\": \"" ;
833- const char * json_postamble = "\"}\n" ;
834- // Ignore write failures because there is nothing we can do
835- write (jl_sig_fd , json_preamble_p1 , strlen (json_preamble_p1 ));
836- write (jl_sig_fd , timestamp_buffer , strlen (timestamp_buffer ));
837- write (jl_sig_fd , json_preamble_p2 , strlen (json_preamble_p2 ));
838- // JSON escape the input string
839- for (size_t i = 0 ; i < strlen (buf ); i += 1 ) {
847+ wlen += strftime (& wbuf [wlen ], 42 , "%Y-%m-%dT%H:%M:%S" , tm_info );
848+ sprintf (& wbuf [wlen ], ".%03ld" , (long )tv .tv_usec / 1000 );
849+ wlen += 4 ;
850+
851+ // JSON preamble to message (15 bytes)
852+ wlen += copystp (& wbuf [wlen ], "\", \"message\": \"" );
853+
854+ // Message
855+ // Each iteration will advance wlen by 1 or 2
856+ for (size_t i = 0 ; i < buflen ; i ++ ) {
857+ // Truncate the message if the write buffer is full
858+ if (wlen == max_b || wlen == max_b - 1 ) {
859+ wlen += copystp (& wbuf [wlen ], "..." );
860+ break ;
861+ }
840862 switch (buf [i ]) {
841863 case '"' :
842- write ( jl_sig_fd , "\\\"" , 2 );
864+ wlen += copystp ( & wbuf [ wlen ] , "\\\"" );
843865 break ;
844866 case '\b' :
845- write ( jl_sig_fd , "\\b" , 2 );
867+ wlen += copystp ( & wbuf [ wlen ] , "\\b" );
846868 break ;
847869 case '\n' :
848- write ( jl_sig_fd , "\\n" , 2 );
870+ wlen += copystp ( & wbuf [ wlen ] , "\\n" );
849871 break ;
850872 case '\r' :
851- write ( jl_sig_fd , "\\r" , 2 );
873+ wlen += copystp ( & wbuf [ wlen ] , "\\r" );
852874 break ;
853875 case '\t' :
854- write ( jl_sig_fd , "\\t" , 2 );
876+ wlen += copystp ( & wbuf [ wlen ] , "\\t" );
855877 break ;
856878 case '\\' :
857- write ( jl_sig_fd , "\\\\" , 2 );
879+ wlen += copystp ( & wbuf [ wlen ] , "\\\\" );
858880 break ;
859881 default :
860- write (jl_sig_fd , buf + i , 1 );
882+ wbuf [wlen ++ ] = buf [i ];
883+ break ;
861884 }
862885 }
863- write (jl_sig_fd , json_postamble , strlen (json_postamble ));
886+ // JSON completion (3 bytes)
887+ wlen += copystp (& wbuf [wlen ], "\"}\n" );
888+ write (jl_sig_fd , wbuf , wlen );
864889 fdatasync (jl_sig_fd );
865890}
866891
892+ extern int jl_inside_heartbeat_thread (void );
893+
867894JL_DLLEXPORT void jl_safe_printf (const char * fmt , ...)
868895{
869896 static char buf [1000 ];
@@ -880,8 +907,11 @@ JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...)
880907 va_end (args );
881908
882909 buf [999 ] = '\0' ;
883- if (jl_inside_signal_handler () && jl_sig_fd != 0 ) {
884- print_error_msg_as_json (buf );
910+ // order is important here: we want to ensure that the threading infra
911+ // has been initialized before we start trying to print to the
912+ // safe crash log file
913+ if (jl_sig_fd != 0 && (jl_inside_signal_handler () || jl_inside_heartbeat_thread ())) {
914+ write_to_safe_crash_log (buf );
885915 }
886916 if (write (STDERR_FILENO , buf , strlen (buf )) < 0 ) {
887917 // nothing we can do; ignore the failure
0 commit comments