@@ -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