LCOV - code coverage report
Current view: top level - flamenco/log_collector - fd_log_collector.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 208 259 80.3 %
Date: 2024-11-13 11:58:15 Functions: 60 900 6.7 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_log_collector_fd_log_collector_h
       2             : #define HEADER_fd_src_flamenco_log_collector_fd_log_collector_h
       3             : 
       4             : #include "fd_log_collector_base.h"
       5             : #include "../runtime/context/fd_exec_instr_ctx.h"
       6             : #include "../runtime/context/fd_exec_txn_ctx.h"
       7             : #include "../../ballet/base58/fd_base58.h"
       8             : #include "../../ballet/base64/fd_base64.h"
       9             : #include <stdio.h>
      10             : #include <stdarg.h>
      11             : 
      12             : /* Log collector + stable log implementations.
      13             :    https://github.com/anza-xyz/agave/blob/v2.0.6/program-runtime/src/log_collector.rs
      14             :    https://github.com/anza-xyz/agave/blob/v2.0.6/program-runtime/src/stable_log.rs */
      15             : 
      16             : /* INTERNALS
      17             :    Internal functions, don't use directly. */
      18             : 
      19        9366 : #define FD_EXEC_LITERAL(STR) ("" STR), (sizeof(STR)-1)
      20             : 
      21             : /* fd_log_collector_private_push pushes a log (internal, don't use directly).
      22             : 
      23             :    This function stores a log msg of size msg_sz, serialized as protobuf.
      24             :    For the high-level functionality, see fd_log_collector_msg().
      25             : 
      26             :    Internally, each log msg is serialized as a 1 byte tag + 1 or 2 bytes
      27             :    msg_sz (variable int < 32768) + msg_sz bytes of the actual msg.
      28             : 
      29             :      |  tag   |    msg_sz    |     msg      |
      30             :      | 1-byte | 1-or-2 bytes | msg_sz bytes |
      31             : 
      32             :    The advantage of this representation is that when we have to store
      33             :    txn metadata in blockstore, we don't have to do any conversion for logs,
      34             :    just copy the entire buffer. */
      35             : static inline void
      36             : fd_log_collector_private_push( fd_log_collector_t * log,
      37             :                                char const *         msg,
      38        9444 :                                ulong                msg_sz ) {
      39        9444 :   uchar * buf   = log->buf;
      40        9444 :   ulong   buf_sz = log->buf_sz;
      41             : 
      42             :   /* Store tag + msg_sz */
      43        9444 :   ulong needs_2b  = (msg_sz>0x7F);
      44        9444 :   buf[ buf_sz   ] = FD_LOG_COLLECTOR_PROTO_TAG;
      45        9444 :   buf[ buf_sz+1 ] = (uchar)( (msg_sz&0x7F) | (needs_2b<<7) );
      46        9444 :   buf[ buf_sz+2 ] = (uchar)( (msg_sz>>7) & 0x7F ); /* This gets overwritten if 0 */
      47             : 
      48             :   /* Copy msg and update total buf_sz */
      49        9444 :   ulong msg_start = buf_sz + 2 + needs_2b;
      50        9444 :   fd_memcpy( buf + msg_start, msg, msg_sz );
      51        9444 :   log->buf_sz = (ushort)( msg_start + msg_sz );
      52        9444 : }
      53             : 
      54             : /* fd_log_collector_private_debug prints all logs (internal, don't use directly). */
      55             : static inline void
      56             : fd_log_collector_private_debug( fd_log_collector_t const * log );
      57             : 
      58             : FD_PROTOTYPES_BEGIN
      59             : 
      60             : /* LOG COLLECTOR API
      61             :    Init, delete... */
      62             : 
      63             : /* fd_log_collector_init initializes a log collector. */
      64             : static inline void
      65      102780 : fd_log_collector_init( fd_log_collector_t * log, int enabled ) {
      66      102780 :   log->buf_sz = 0;
      67      102780 :   log->log_sz = 0;
      68      102780 :   log->warn = 0;
      69      102780 :   log->disabled = !enabled;
      70      102780 : }
      71             : 
      72             : static inline ulong
      73             : fd_log_collector_check_and_truncate( fd_log_collector_t * log,
      74      188658 :                                      ulong                msg_sz ) {
      75      188658 :   ulong bytes_written = fd_ulong_sat_add( log->log_sz, msg_sz );
      76      188658 :   int ret = bytes_written >= FD_LOG_COLLECTOR_MAX;
      77      188658 :   if( FD_UNLIKELY( ret ) ) {
      78          18 :     if( FD_UNLIKELY( !log->warn ) ) {
      79          18 :       log->warn = 1;
      80          18 :       fd_log_collector_private_push( log, FD_EXEC_LITERAL( "Log truncated" ) );
      81          18 :     }
      82          18 :     return ULONG_MAX;
      83          18 :   }
      84      188640 :   return bytes_written;
      85      188658 : }
      86             : 
      87             : /* fd_log_collector_delete deletes a log collector. */
      88             : static inline void
      89           0 : fd_log_collector_delete( fd_log_collector_t const * log ) {
      90           0 :   (void)log;
      91             : #ifdef VLOG
      92             :   if( FD_LIKELY( log->disabled ) ) {
      93             :     return;
      94             :   }
      95             :   fd_log_collector_private_debug( log );
      96             : #endif
      97           0 : }
      98             : 
      99             : /* LOG COLLECTOR MSG API
     100             : 
     101             :    Analogous of Agave's ic_msg!():
     102             :    https://github.com/anza-xyz/agave/blob/v2.0.6/program-runtime/src/log_collector.rs
     103             : 
     104             :    - fd_log_collector_msg
     105             :    - fd_log_collector_msg_literal
     106             :    - fd_log_collector_msg_many
     107             :    - fd_log_collector_printf_*
     108             : */
     109             : 
     110             : /* fd_log_collector_msg logs msg of size msg_sz.
     111             :    This is analogous of Agave's ic_msg!() / ic_logger_msg!().
     112             : 
     113             :    msg is expected to be a valid utf8 string, it's responsibility
     114             :    of the caller to enforce that.  msg doesn't have to be \0 terminated
     115             :    and can contain \0 within.  Most logs are cstr, base58/64, so
     116             :    they are utf8.  For an example of log from user input, see the
     117             :    sol_log_() syscall where we use fd_utf8_validate().
     118             : 
     119             :    if msg is a cstr, for compatibility with rust, msg_sz is the msg
     120             :    length (not the size of the buffer), and the final \0 should not
     121             :    be included in logs.  For literals, use fd_log_collector_msg_literal().
     122             : 
     123             :    msg_sz==0 is ok, however it's important to understand that log
     124             :    collector is an interface to developers, not exposed to users.
     125             :    Users can, for example, log inside BPF programs using msg!(), that
     126             :    gets translated to the syscall sol_log_(), that in turn appends
     127             :    a log of the form "Program log: ...". So msg_sz, realistically,
     128             :    is never 0 nor small.  This is important for our implementation,
     129             :    to keep serialization overhead low.
     130             : 
     131             :    When msg is made of multiple disjoing buffers, we should use
     132             :    fd_log_collector_msg_many(), and implement more variants as
     133             :    needed.  The core idea is very simple: we know the total msg_sz,
     134             :    we decide if the log needs to be included or truncated, and
     135             :    if we include the logs we will copy the actual content
     136             :    from multiple places.  This should be the correct and high
     137             :    performance way to log.
     138             : 
     139             :    For ease of development, and because logs in runtime, vm,
     140             :    syscalls, native programs, etc. are what they are, we also
     141             :    implemented fd_log_collector_printf_*().  These are
     142             :    dangerous to use, especially given the way we serialize
     143             :    logs on-the-fly.  Prefer fd_log_collector_msg_* wherever
     144             :    possible. */
     145             : static inline void
     146             : fd_log_collector_msg( fd_exec_instr_ctx_t * ctx,
     147             :                       char const *          msg,
     148       10929 :                       ulong                 msg_sz ) {
     149       10929 :   fd_log_collector_t * log = &ctx->txn_ctx->log_collector;
     150       10929 :   if( FD_LIKELY( log->disabled ) ) {
     151        1503 :     return;
     152        1503 :   }
     153             : 
     154        9426 :   ulong bytes_written = fd_log_collector_check_and_truncate( log, msg_sz );
     155        9426 :   if( FD_LIKELY( bytes_written < ULONG_MAX ) ) {
     156        9426 :     log->log_sz = (ushort)bytes_written;
     157        9426 :     fd_log_collector_private_push( log, msg, msg_sz );
     158        9426 :   }
     159        9426 : }
     160             : 
     161             : /* fd_log_collector_msg_literal logs the literal (const cstr) msg,
     162             :    handling size.  See fd_log_collector_msg() for details. */
     163        9348 : #define fd_log_collector_msg_literal( ctx, log ) fd_log_collector_msg( ctx, FD_EXEC_LITERAL( log ) )
     164             : 
     165             : /* fd_log_collector_msg_many logs a msg supplied as many
     166             :    buffers.  msg := msg0 | msg1 | ... | msgN
     167             :    
     168             :    num_buffers informs the number of (char const * msg, ulong sz) pairs
     169             :    in the function call.
     170             :    NOTE: you must explicitly pass in ulong values for sz, either by cast
     171             :    or with the UL literal. va_args behaves weirdly otherwise */
     172             : static inline void
     173       78342 : fd_log_collector_msg_many( fd_exec_instr_ctx_t * ctx, int num_buffers, ... ) {
     174       78342 :   fd_log_collector_t * log = &ctx->txn_ctx->log_collector;
     175       78342 :   if( FD_LIKELY( log->disabled ) ) {
     176         831 :     return;
     177         831 :   }
     178             : 
     179       77511 :   va_list args;
     180       77511 :   va_start( args, num_buffers );
     181             : 
     182             :   /* Calculate the total message size and check for overflow */
     183       77511 :   ulong msg_sz = 0;
     184      232578 :   for( int i = 0; i < num_buffers; i++ ) {
     185      155067 :       va_arg( args, char const * );
     186      155067 :       ulong msg_sz_part = va_arg( args, ulong );
     187      155067 :       msg_sz = fd_ulong_sat_add( msg_sz, msg_sz_part );
     188      155067 :   }
     189       77511 :   va_end( args );
     190       77511 :   ulong bytes_written = fd_log_collector_check_and_truncate( log, msg_sz );
     191       77511 :   if( FD_LIKELY( bytes_written < ULONG_MAX ) ) {
     192       77502 :     log->log_sz = (ushort)bytes_written;
     193             : 
     194       77502 :     uchar * buf    = log->buf;
     195       77502 :     ulong   buf_sz = log->buf_sz;
     196             : 
     197             :     /* Store tag + msg_sz */
     198       77502 :     ulong needs_2b  = (msg_sz>0x7F);
     199       77502 :     buf[ buf_sz ]   = FD_LOG_COLLECTOR_PROTO_TAG;
     200       77502 :     buf[ buf_sz+1 ] = (uchar)( (msg_sz&0x7F) | (needs_2b<<7) );
     201       77502 :     buf[ buf_sz+2 ] = (uchar)( (msg_sz>>7) & 0x7F ); /* This gets overwritten if 0 */
     202             : 
     203             :     /* Copy all messages and update total buf_sz */
     204       77502 :     ulong buf_start = buf_sz + 2 + needs_2b;
     205       77502 :     ulong offset = buf_start;
     206             : 
     207       77502 :     va_start(args, num_buffers);  // Restart argument list traversal
     208      232551 :     for (int i = 0; i < num_buffers; i++) {
     209      155049 :         char const *msg = va_arg( args, char const * );
     210      155049 :         ulong msg_sz_part = va_arg( args, ulong );
     211      155049 :         fd_memcpy( buf + offset, msg, msg_sz_part );
     212      155049 :         offset += msg_sz_part;
     213      155049 :     }
     214       77502 :     va_end(args);
     215       77502 :     log->buf_sz = (ushort)offset;
     216       77502 :   }
     217       77511 : }
     218             : 
     219      101649 : #define FD_LOG_COLLECTOR_PRINTF_MAX_1B 128
     220           0 : #define FD_LOG_COLLECTOR_PRINTF_MAX_2B 2000
     221             : FD_STATIC_ASSERT( 2*FD_LOG_COLLECTOR_PRINTF_MAX_2B <= FD_LOG_COLLECTOR_EXTRA, "Increase FD_LOG_COLLECTOR_EXTRA" );
     222             : 
     223             : /* fd_log_collector_printf_dangerous_max_127() logs a message
     224             :    supplied as a formatting string with params.
     225             : 
     226             :    This is dangerous and should only be used when we can
     227             :    guarantee that the total log msg_sz <= 127.
     228             : 
     229             :    See also fd_log_collector_printf_dangerous_128_to_2k() for
     230             :    larger logs, and see fd_log_collector_program_return() for
     231             :    an example on how to deal with msg_sz.
     232             : 
     233             :    This implementation uses vsnprintf() to directly write into
     234             :    the log buf *before* deciding if the log should be included
     235             :    or not.  As a result of vsnprintf() we get msg_sz, and then
     236             :    we can decide to actually insert the log or truncate.  Since
     237             :    we serialize msg_sz as a variable int, we must guarantee
     238             :    that msg_sz <= 127, i.e. fits in 1 byte, otherwise we'd have
     239             :    to memmove the log msg. */
     240             : __attribute__ ((format (printf, 2, 3)))
     241             : static inline void
     242             : fd_log_collector_printf_dangerous_max_127( fd_exec_instr_ctx_t * ctx,
     243      105771 :                                            char const * fmt, ... ) {
     244      105771 :   fd_log_collector_t * log = &ctx->txn_ctx->log_collector;
     245      105771 :   if( FD_LIKELY( log->disabled ) ) {
     246        4122 :     return;
     247        4122 :   }
     248             : 
     249      101649 :   uchar * buf    = log->buf;
     250      101649 :   ulong   buf_sz = log->buf_sz;
     251             : 
     252             :   /* Store the log at buf_sz+2 (1 byte tag + 1 byte msg_sz), and retrieve
     253             :      the final msg_sz. */
     254      101649 :   va_list ap;
     255      101649 :   va_start( ap, fmt );
     256      101649 :   int res = vsnprintf( (char *)(buf + buf_sz + 2), FD_LOG_COLLECTOR_PRINTF_MAX_1B, fmt, ap );
     257      101649 :   va_end( ap );
     258             : 
     259             :   /* We use vsnprintf to protect against oob writes, however it should never
     260             :      truncate.  If truncate happens, it means that we're using
     261             :      fd_log_collector_printf_dangerous_max_127(), incorrectly for example
     262             :      with a "%s" and an unbound variable (user input, var that's not
     263             :      null-terminated cstr, ...).
     264             :      We MUST only use fd_log_collector_printf_dangerous_max_127()
     265             :      as a convenince method, when we can guarantee that the total msg_sz is
     266             :      bound by FD_LOG_COLLECTOR_PRINTF_MAX_1B. */
     267      101649 :   FD_TEST_CUSTOM( res>=0 && res<FD_LOG_COLLECTOR_PRINTF_MAX_1B,
     268      101649 :     "A transaction log was truncated unexpectedly. Please report to developers." );
     269             : 
     270             :   /* Decide if we should include the log or truncate. */
     271      101649 :   ulong msg_sz = (ulong)res;
     272      101649 :   ulong bytes_written = fd_log_collector_check_and_truncate( log, msg_sz );
     273      101649 :   if( FD_LIKELY( bytes_written < ULONG_MAX ) ) {
     274             :     /* Insert log: store tag + msg_sz (1 byte) and update buf_sz */
     275      101649 :     log->log_sz = (ushort)bytes_written;
     276      101649 :     buf[ buf_sz   ] = FD_LOG_COLLECTOR_PROTO_TAG;
     277      101649 :     buf[ buf_sz+1 ] = (uchar)( msg_sz & 0x7F );
     278      101649 :     log->buf_sz = (ushort)( buf_sz + msg_sz + 2 );
     279      101649 :   }
     280      101649 : }
     281             : 
     282             : /* fd_log_collector_printf_dangerous_128_to_2k() logs a message
     283             :    supplied as a formatting string with params.
     284             : 
     285             :    This is dangerous and should only be used when we can
     286             :    guarantee that the total log 128 <= msg_sz < 2,000.
     287             : 
     288             :    This implementation uses vsnprintf() to directly write into
     289             :    the log buf *before* deciding if the log should be included
     290             :    or not.  As a result of vsnprintf() we get msg_sz, and then
     291             :    we can decide to actually insert the log or truncate.  Since
     292             :    we serialize msg_sz as a variable int, we must guarantee
     293             :    that 128 <= msg_sz < 32758, i.e. fits in 2 byte, otherwise
     294             :    we'd have to memmove the log msg.
     295             : 
     296             :    Moreover, we need to guarantee that the log buf is big enough
     297             :    to fit the log msg.  Hence we further limit msg_sz < 2000. */
     298             : __attribute__ ((format (printf, 2, 3)))
     299             : static inline void
     300             : fd_log_collector_printf_dangerous_128_to_2k( fd_exec_instr_ctx_t * ctx,
     301           0 :                                              char const * fmt, ... ) {
     302           0 :   fd_log_collector_t * log = &ctx->txn_ctx->log_collector;
     303           0 :   if( FD_LIKELY( log->disabled ) ) {
     304           0 :     return;
     305           0 :   }
     306             : 
     307           0 :   uchar * buf    = log->buf;
     308           0 :   ulong   buf_sz = log->buf_sz;
     309             : 
     310             :   /* Store the log at buf_sz+3 (1 byte tag + 2 bytes msg_sz), and retrieve
     311             :      the final msg_sz. */
     312           0 :   va_list ap;
     313           0 :   va_start( ap, fmt );
     314           0 :   int res = vsnprintf( (char *)(buf + buf_sz + 3), FD_LOG_COLLECTOR_PRINTF_MAX_2B, fmt, ap );
     315           0 :   va_end( ap );
     316             :   /* We use vsnprintf to protect against oob writes, however it should never
     317             :      truncate.  If truncate happens, it means that we're using
     318             :      fd_log_collector_printf_dangerous_max_127(), incorrectly for example
     319             :      with a "%s" and an unbound variable (user input, var that's not
     320             :      null-terminated cstr, ...).
     321             :      We MUST only use fd_log_collector_printf_dangerous_max_127()
     322             :      as a convenince method, when we can guarantee that the total msg_sz is
     323             :      bound by FD_LOG_COLLECTOR_PRINTF_MAX_2B. */
     324           0 :   FD_TEST_CUSTOM( res>=FD_LOG_COLLECTOR_PRINTF_MAX_1B && res<FD_LOG_COLLECTOR_PRINTF_MAX_2B,
     325           0 :     "A transaction log was truncated unexpectedly. Please report to developers." );
     326             : 
     327             :   /* Decide if we should include the log or truncate. */
     328           0 :   ulong msg_sz = (ulong)res;
     329           0 :   ulong bytes_written = fd_log_collector_check_and_truncate( log, msg_sz );
     330           0 :   if( FD_LIKELY( bytes_written < ULONG_MAX ) ) {
     331             :     /* Insert log: store tag + msg_sz (2 bytes) and update buf_sz */
     332           0 :     log->log_sz = (ushort)bytes_written;
     333           0 :     buf[ buf_sz   ] = FD_LOG_COLLECTOR_PROTO_TAG;
     334           0 :     buf[ buf_sz+1 ] = (uchar)( (msg_sz&0x7F) | (1<<7) );
     335           0 :     buf[ buf_sz+2 ] = (uchar)( (msg_sz>>7) & 0x7F );
     336           0 :     log->buf_sz = (ushort)( buf_sz + msg_sz + 3 );
     337           0 :   }
     338           0 : }
     339             : 
     340             : /* fd_log_collector_printf_inefficient_max_512() logs a message
     341             :    supplied as a formatting string with params.
     342             : 
     343             :    This is inefficient because it uses an external buffer and
     344             :    essentially does 2 memcpy instead of 1, however it reduces
     345             :    the complexity when msg_sz can be below or above 127, for
     346             :    example in many error messages where we have to print 2
     347             :    pubkeys. */
     348             : __attribute__ ((format (printf, 2, 3)))
     349             : static inline void
     350             : fd_log_collector_printf_inefficient_max_512( fd_exec_instr_ctx_t * ctx,
     351        1518 :                                              char const * fmt, ... ) {
     352        1518 :   char msg[ 512 ];
     353             : 
     354        1518 :   va_list ap;
     355        1518 :   va_start( ap, fmt );
     356        1518 :   int msg_sz = vsnprintf( msg, sizeof(msg), fmt, ap );
     357        1518 :   va_end( ap );
     358             : 
     359        1518 :   FD_TEST_CUSTOM( msg_sz>=0 && (ulong)msg_sz<sizeof(msg),
     360        1518 :     "A transaction log was truncated unexpectedly. Please report to developers." );
     361             : 
     362        1518 :   fd_log_collector_msg( ctx, msg, (ulong)msg_sz );
     363        1518 : }
     364             : 
     365             : /* STABLE LOG
     366             : 
     367             :    Analogous of Agave's stable_log interface:
     368             :    https://github.com/anza-xyz/agave/blob/v2.0.6/program-runtime/src/stable_log.rs
     369             : 
     370             :    - program_invoke
     371             :    - program_log
     372             :    - program_data -- implemented in fd_vm_syscall_sol_log_data()
     373             :    - program_return
     374             :    - program_success
     375             :    - program_failure
     376             :    - program_consumed */
     377             : 
     378             : /* fd_log_collector_program_invoke logs:
     379             :      "Program <ProgramIdBase58> invoke [<n>]"
     380             : 
     381             :    This function is called at the beginning of every instruction.
     382             :    Other logs (notably success/failure) also write <ProgramIdBase58>,
     383             :    so this function precomputes it and stores it inside the instr_ctx. */
     384             : static inline void
     385       93564 : fd_log_collector_program_invoke( fd_exec_instr_ctx_t * ctx ) {
     386       93564 :   if( FD_LIKELY( ctx->txn_ctx->log_collector.disabled ) ) {
     387        6951 :     return;
     388        6951 :   }
     389             : 
     390             :   /* Cache ctx->program_id_base58 */
     391       86613 :   fd_base58_encode_32( ctx->instr->program_id_pubkey.uc, NULL, ctx->program_id_base58 );
     392             :   /* Max msg_sz: 22 - 4 + 44 + 10 = 72 < 127 => we can use printf */
     393       86613 :   fd_log_collector_printf_dangerous_max_127( ctx, "Program %s invoke [%u]", ctx->program_id_base58, ctx->depth+1 );
     394       86613 : }
     395             : 
     396             : /* fd_log_collector_program_log logs:
     397             :      "Program <ProgramIdBase58> log: <msg>"
     398             : 
     399             :    msg must be a valid utf8 string, it's responsibility of the caller to
     400             :    validate that.  This is the implementation underlying _sol_log() syscall. */
     401             : static inline void
     402         873 : fd_log_collector_program_log( fd_exec_instr_ctx_t * ctx, char const * msg, ulong msg_sz ) {
     403         873 :   fd_log_collector_msg_many( ctx, 2, "Program log: ", 13UL, msg, msg_sz );
     404         873 : }
     405             : 
     406             : /* fd_log_collector_program_return logs:
     407             :      "Program return: <ProgramIdBase58> <dataAsBase64>"
     408             : 
     409             :    Since return data is at most 1024 bytes, it's base64 representation is
     410             :    at most 1368 bytes and msg_sz is known in advance, thus we can use
     411             :    fd_log_collector_printf_*.
     412             : 
     413             :    TODO: implement based on fd_log_collector_msg_many(). */
     414             : static inline void
     415           0 : fd_log_collector_program_return( fd_exec_instr_ctx_t * ctx ) {
     416           0 :   if( FD_LIKELY( ctx->txn_ctx->log_collector.disabled ) ) {
     417           0 :     return;
     418           0 :   }
     419             : 
     420             :   /* ctx->txn_ctx->return_data is 1024 bytes max, so its base64 repr
     421             :      is at most (1024+2)/3*4 bytes, plus we use 1 byte for \0. */
     422           0 :   char return_base64[ (sizeof(ctx->txn_ctx->return_data.data)+2)/3*4+1 ];
     423           0 :   ulong sz = fd_base64_encode( return_base64, ctx->txn_ctx->return_data.data, ctx->txn_ctx->return_data.len );
     424           0 :   return_base64[ sz ] = 0;
     425             :   /* Max msg_sz: 21 - 4 + 44 + 1368 = 1429 < 1500 => we can use printf, but have to handle sz */
     426           0 :   ulong msg_sz = 17 + strlen(ctx->program_id_base58) + sz;
     427           0 :   if( msg_sz<=127 ) {
     428           0 :     fd_log_collector_printf_dangerous_max_127( ctx, "Program return: %s %s", ctx->program_id_base58, return_base64 );
     429           0 :   } else {
     430           0 :     fd_log_collector_printf_dangerous_128_to_2k( ctx, "Program return: %s %s", ctx->program_id_base58, return_base64 );
     431           0 :   }
     432           0 : }
     433             : 
     434             : /* fd_log_collector_program_success logs:
     435             :      "Program <ProgramIdBase58> success" */
     436             : static inline void
     437       13377 : fd_log_collector_program_success( fd_exec_instr_ctx_t * ctx ) {
     438             :   /* Max msg_sz: 18 - 2 + 44 = 60 < 127 => we can use printf */
     439       13377 :   fd_log_collector_printf_dangerous_max_127( ctx, "Program %s success", ctx->program_id_base58 );
     440       13377 : }
     441             : 
     442             : /* fd_log_collector_program_success logs:
     443             :      "Program <ProgramIdBase58> failed: <err>"
     444             : 
     445             :    This function handles the logic to log the correct msg, based
     446             :    on the type of error (InstructionError, SyscallError...).
     447             :    https://github.com/anza-xyz/agave/blob/v2.0.6/program-runtime/src/invoke_context.rs#L535-L549
     448             : 
     449             :    The error msg is obtained by external functions, e.g. fd_vm_syscall_strerror(),
     450             :    and can be either a valid msg or an empty string.  Empty string represents
     451             :    special handling of the error log, for example the syscall panic logs directly
     452             :    the result, and therefore can be skipped at this stage. */
     453             : static inline void
     454       81405 : fd_log_collector_program_failure( fd_exec_instr_ctx_t * ctx ) {
     455       81405 :   if( FD_LIKELY( ctx->txn_ctx->log_collector.disabled ) ) {
     456        4098 :     return;
     457        4098 :   }
     458             : 
     459       77307 :   extern char const * fd_vm_ebpf_strerror( int err );
     460       77307 :   extern char const * fd_vm_syscall_strerror( int err );
     461       77307 :   extern char const * fd_executor_instr_strerror( int err );
     462             : 
     463       77307 :   char custom_err[33] = { 0 };
     464       77307 :   const char * err = custom_err;
     465       77307 :   const fd_exec_txn_ctx_t * txn_ctx = ctx->txn_ctx;
     466       77307 :   if( txn_ctx->custom_err != UINT_MAX ) {
     467             :     /* Max msg_sz = 32 <= 66 */
     468        5598 :     snprintf( custom_err, sizeof(custom_err), "custom program error: 0x%x", txn_ctx->custom_err );
     469       71709 :   } else if( txn_ctx->exec_err ) {
     470       71709 :     switch( txn_ctx->exec_err_kind ) {
     471         183 :       case FD_EXECUTOR_ERR_KIND_SYSCALL:
     472         183 :         err = fd_vm_syscall_strerror( txn_ctx->exec_err );
     473         183 :         break;
     474       70752 :       case FD_EXECUTOR_ERR_KIND_INSTR:
     475       70752 :         err = fd_executor_instr_strerror( txn_ctx->exec_err );
     476       70752 :         break;
     477         774 :       default:
     478         774 :         err = fd_vm_ebpf_strerror( txn_ctx->exec_err );
     479       71709 :     }
     480       71709 :   }
     481             : 
     482             :   /* Skip empty string, this means that the msg has already been logged. */
     483       77307 :   if( FD_LIKELY( err[0] ) ) {
     484       77307 :     char err_prefix[ 17+FD_BASE58_ENCODED_32_SZ ]; // 17==strlen("Program  failed: ")
     485       77307 :     int err_prefix_len = sprintf( err_prefix, "Program %s failed: ", ctx->program_id_base58 );
     486       77307 :     if( err_prefix_len > 0 ) {
     487             :       /* Equivalent to: "Program %s failed: %s" */
     488       77307 :       fd_log_collector_msg_many( ctx, 2, err_prefix, (ulong)err_prefix_len, err, (ulong)strlen(err) );
     489       77307 :     }
     490       77307 :   }
     491       77307 : }
     492             : 
     493             : /* fd_log_collector_program_consumed logs:
     494             :      "Program <ProgramIdBase58> consumed <consumed> of <tota> compute units" */
     495             : static inline void
     496        1017 : fd_log_collector_program_consumed( fd_exec_instr_ctx_t * ctx, ulong consumed, ulong total ) {
     497             :   /* Max msg_sz: 44 - 8 + 44 + 20 + 20 = 120 < 127 => we can use printf */
     498        1017 :   fd_log_collector_printf_dangerous_max_127( ctx, "Program %s consumed %lu of %lu compute units", ctx->program_id_base58, consumed, total );
     499        1017 : }
     500             : 
     501             : /* DEBUG
     502             :    Only used for testing (inefficient but ok). */
     503             : 
     504             : static inline ushort
     505        1290 : fd_log_collector_debug_get_msg_sz( uchar const ** buf ) {
     506        1290 :   uchar msg0 = (*buf)[1];
     507        1290 :   uchar msg1 = (*buf)[2]; /* This is never oob */
     508        1290 :   int needs_2b = (msg0>0x7F);
     509        1290 :   ushort msg_sz = fd_ushort_if( needs_2b, (ushort)(((ushort)(msg1) << 7)|(msg0 & 0x7F)), (ushort)msg0 );
     510        1290 :   *buf += 2 + needs_2b;
     511        1290 :   return msg_sz;
     512        1290 : }
     513             : 
     514             : static inline ulong
     515          30 : fd_log_collector_debug_len( fd_log_collector_t const * log ) {
     516          30 :   ulong len = 0;
     517          63 :   for( uchar const * cur = log->buf; cur < log->buf + log->buf_sz; ) {
     518          33 :     ushort cur_sz = fd_log_collector_debug_get_msg_sz( &cur );
     519          33 :     cur += cur_sz;
     520          33 :     ++len;
     521          33 :   }
     522          30 :   return len;
     523          30 : }
     524             : 
     525             : static inline uchar const *
     526             : fd_log_collector_debug_get( fd_log_collector_t const * log,
     527             :                             ulong                      log_num,
     528             :                             uchar const **             msg,
     529          15 :                             ulong *                    msg_sz ) {
     530          15 :   uchar const * cur = log->buf;
     531          15 :   ushort cur_sz = 0;
     532             : 
     533          15 :   cur_sz = fd_log_collector_debug_get_msg_sz( &cur );
     534          24 :   while( log_num>0 ) {
     535           9 :     cur += cur_sz;
     536           9 :     cur_sz = fd_log_collector_debug_get_msg_sz( &cur );
     537           9 :     --log_num;
     538           9 :   }
     539          15 :   if( msg )    *msg    = cur;
     540          15 :   if( msg_sz ) *msg_sz = cur_sz;
     541          15 :   return cur;
     542          15 : }
     543             : 
     544             : static inline ulong
     545             : fd_log_collector_debug_sprintf( fd_log_collector_t const * log,
     546             :                                 char *                     out,
     547        1206 :                                 int                        filter_zero ) {
     548        1206 :   ulong out_sz = 0;
     549             : 
     550        1206 :   ulong pos = 0;
     551        1206 :   uchar const * buf = log->buf;
     552        2439 :   while( pos < log->buf_sz ) {
     553             :     /* Read cur string sz */
     554        1233 :     ushort cur_sz = fd_log_collector_debug_get_msg_sz( &buf );
     555             : 
     556             :     /* Copy string and add \n.
     557             :        Slow version of memcpy that skips \0, because a \0 can be in logs.
     558             :        Equivalent to:
     559             :        fd_memcpy( out + out_sz, buf, cur_sz ); out_sz += cur_sz; */
     560        1233 :     if( filter_zero ) {
     561           0 :       for( ulong i=0; i<cur_sz; i++ ) {
     562           0 :         if( buf[i] ) {
     563           0 :           out[ out_sz++ ] = (char)buf[i];
     564           0 :         }
     565           0 :       }
     566        1233 :     } else {
     567        1233 :       fd_memcpy( out+out_sz, buf, cur_sz );
     568        1233 :       out_sz += cur_sz;
     569        1233 :     }
     570        1233 :     out[ out_sz++ ] = '\n';
     571             : 
     572             :     /* Move to next str */
     573        1233 :     buf += cur_sz;
     574        1233 :     pos = (ulong)(buf - log->buf);
     575        1233 :   }
     576             : 
     577             :   /* Remove the last \n, or return empty cstr */
     578        1206 :   out_sz = out_sz ? out_sz-1 : 0;
     579        1206 :   out[ out_sz ] = '\0';
     580        1206 :   return out_sz;
     581        1206 : }
     582             : 
     583             : static inline void
     584           0 : fd_log_collector_private_debug( fd_log_collector_t const * log ) {
     585           0 :   char out[FD_LOG_COLLECTOR_MAX + FD_LOG_COLLECTOR_EXTRA];
     586           0 :   fd_log_collector_debug_sprintf( log, out, 1 );
     587           0 :   FD_LOG_WARNING(( "\n-----\n%s\n-----", out ));
     588           0 : }
     589             : 
     590             : FD_PROTOTYPES_END
     591             : 
     592             : #endif /* HEADER_fd_src_flamenco_log_collector_fd_log_collector_h */

Generated by: LCOV version 1.14