@@ -813,56 +813,83 @@ JL_DLLEXPORT int jl_printf(uv_stream_t *s, const char *format, ...)
813
813
return c ;
814
814
}
815
815
816
- STATIC_INLINE void print_error_msg_as_json (char * buf ) JL_NOTSAFEPOINT
816
+ STATIC_INLINE int copystp (char * dest , const char * src )
817
817
{
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)
820
842
struct timeval tv ;
821
843
struct tm * tm_info ;
822
- char timestamp_buffer [50 ];
823
- // Get current time
824
844
gettimeofday (& tv , NULL );
825
845
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
+ }
839
861
switch (buf [i ]) {
840
862
case '"' :
841
- write ( jl_sig_fd , "\\\"" , 2 );
863
+ wlen += copystp ( & wbuf [ wlen ] , "\\\"" );
842
864
break ;
843
865
case '\b' :
844
- write ( jl_sig_fd , "\\b" , 2 );
866
+ wlen += copystp ( & wbuf [ wlen ] , "\\b" );
845
867
break ;
846
868
case '\n' :
847
- write ( jl_sig_fd , "\\n" , 2 );
869
+ wlen += copystp ( & wbuf [ wlen ] , "\\n" );
848
870
break ;
849
871
case '\r' :
850
- write ( jl_sig_fd , "\\r" , 2 );
872
+ wlen += copystp ( & wbuf [ wlen ] , "\\r" );
851
873
break ;
852
874
case '\t' :
853
- write ( jl_sig_fd , "\\t" , 2 );
875
+ wlen += copystp ( & wbuf [ wlen ] , "\\t" );
854
876
break ;
855
877
case '\\' :
856
- write ( jl_sig_fd , "\\\\" , 2 );
878
+ wlen += copystp ( & wbuf [ wlen ] , "\\\\" );
857
879
break ;
858
880
default :
859
- write (jl_sig_fd , buf + i , 1 );
881
+ wbuf [wlen ++ ] = buf [i ];
882
+ break ;
860
883
}
861
884
}
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 );
863
888
fdatasync (jl_sig_fd );
864
889
}
865
890
891
+ extern int jl_inside_heartbeat_thread (void );
892
+
866
893
JL_DLLEXPORT void jl_safe_printf (const char * fmt , ...)
867
894
{
868
895
static char buf [1000 ];
@@ -879,8 +906,11 @@ JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...)
879
906
va_end (args );
880
907
881
908
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 );
884
914
}
885
915
if (write (STDERR_FILENO , buf , strlen (buf )) < 0 ) {
886
916
// nothing we can do; ignore the failure
0 commit comments