LCOV - code coverage report
Current view: top level - flamenco/log_collector - fd_log_collector.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 128 261 49.0 %
Date: 2025-09-19 04:41:14 Functions: 17 2020 0.8 %

          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          12 : #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          15 :                                ulong                msg_sz ) {
      39          15 :   uchar * buf   = log->buf;
      40          15 :   ulong   buf_sz = log->buf_sz;
      41             : 
      42             :   /* Store tag + msg_sz */
      43          15 :   ulong needs_2b  = (msg_sz>0x7F);
      44          15 :   buf[ buf_sz   ] = FD_LOG_COLLECTOR_PROTO_TAG;
      45          15 :   buf[ buf_sz+1 ] = (uchar)( (msg_sz&0x7F) | (needs_2b<<7) );
      46          15 :   buf[ buf_sz+2 ] = (uchar)( (msg_sz>>7) & 0x7F ); /* This gets overwritten if 0 */
      47             : 
      48             :   /* Copy msg and update total buf_sz */
      49          15 :   ulong msg_start = buf_sz + 2 + needs_2b;
      50          15 :   fd_memcpy( buf + msg_start, msg, msg_sz );
      51          15 :   log->buf_sz = (ushort)( msg_start + msg_sz );
      52          15 : }
      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          12 : fd_log_collector_init( fd_log_collector_t * log, int enabled ) {
      66          12 :   log->buf_sz = 0;
      67          12 :   log->log_sz = 0;
      68          12 :   log->warn = 0;
      69          12 :   log->disabled = !enabled;
      70          12 : }
      71             : 
      72             : static inline ulong
      73             : fd_log_collector_check_and_truncate( fd_log_collector_t * log,
      74          27 :                                      ulong                msg_sz ) {
      75          27 :   ulong bytes_written = fd_ulong_sat_add( log->log_sz, msg_sz );
      76          27 :   int ret = bytes_written >= FD_LOG_COLLECTOR_MAX;
      77          27 :   if( FD_UNLIKELY( ret ) ) {
      78           3 :     if( FD_UNLIKELY( !log->warn ) ) {
      79           3 :       log->warn = 1;
      80           3 :       fd_log_collector_private_push( log, FD_EXEC_LITERAL( "Log truncated" ) );
      81           3 :     }
      82           3 :     return ULONG_MAX;
      83           3 :   }
      84          24 :   return bytes_written;
      85          27 : }
      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           0 : }
      92             : 
      93             : /* LOG COLLECTOR MSG API
      94             : 
      95             :    Analogous of Agave's ic_msg!():
      96             :    https://github.com/anza-xyz/agave/blob/v2.0.6/program-runtime/src/log_collector.rs
      97             : 
      98             :    - fd_log_collector_msg
      99             :    - fd_log_collector_msg_literal
     100             :    - fd_log_collector_msg_many
     101             :    - fd_log_collector_printf_*
     102             : */
     103             : 
     104             : /* fd_log_collector_msg logs msg of size msg_sz.
     105             :    This is analogous to Agave's ic_msg!() / ic_logger_msg!().
     106             : 
     107             :    Logs are not recorded on-chain, and are therefore not
     108             :    consensus-critical, however, there exist 3rd party off-chain
     109             :    applications that parses logs, and expects logs to be equivalent to
     110             :    agave.
     111             : 
     112             :    msg is expected to be a valid utf8 string, it is the responsibility
     113             :    of the caller to enforce that.  msg doesn't have to be \0 terminated
     114             :    and can contain \0 within.  Most logs are cstr, base58/64, so
     115             :    they are utf8.  For an example of log from user input, see the
     116             :    sol_log_() syscall where we use fd_utf8_verify().
     117             : 
     118             :    if msg is a cstr, for compatibility with rust, msg_sz is the msg
     119             :    length (not the size of the buffer), and the final \0 should not
     120             :    be included in logs.  For literals, use
     121             :    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 consists of multiple disjoint 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          12 :                       ulong                 msg_sz ) {
     149          12 :   fd_log_collector_t * log = &ctx->txn_ctx->log_collector;
     150          12 :   if( FD_LIKELY( log->disabled ) ) {
     151           0 :     return;
     152           0 :   }
     153             : 
     154          12 :   ulong bytes_written = fd_log_collector_check_and_truncate( log, msg_sz );
     155          12 :   if( FD_LIKELY( bytes_written < ULONG_MAX ) ) {
     156          12 :     log->log_sz = (ushort)bytes_written;
     157          12 :     fd_log_collector_private_push( log, msg, msg_sz );
     158          12 :   }
     159          12 : }
     160             : 
     161             : /* fd_log_collector_msg_literal logs the literal (const cstr) msg,
     162             :    handling size.  See fd_log_collector_msg() for details. */
     163           9 : #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           9 : fd_log_collector_msg_many( fd_exec_instr_ctx_t * ctx, int num_buffers, ... ) {
     174           9 :   fd_log_collector_t * log = &ctx->txn_ctx->log_collector;
     175           9 :   if( FD_LIKELY( log->disabled ) ) {
     176           0 :     return;
     177           0 :   }
     178             : 
     179           9 :   va_list args;
     180           9 :   va_start( args, num_buffers );
     181             : 
     182             :   /* Calculate the total message size and check for overflow */
     183           9 :   ulong msg_sz = 0;
     184          27 :   for( int i = 0; i < num_buffers; i++ ) {
     185          18 :       va_arg( args, char const * );
     186          18 :       ulong msg_sz_part = va_arg( args, ulong );
     187          18 :       msg_sz = fd_ulong_sat_add( msg_sz, msg_sz_part );
     188          18 :   }
     189           9 :   va_end( args );
     190           9 :   ulong bytes_written = fd_log_collector_check_and_truncate( log, msg_sz );
     191           9 :   if( FD_LIKELY( bytes_written < ULONG_MAX ) ) {
     192           6 :     log->log_sz = (ushort)bytes_written;
     193             : 
     194           6 :     uchar * buf    = log->buf;
     195           6 :     ulong   buf_sz = log->buf_sz;
     196             : 
     197             :     /* Store tag + msg_sz */
     198           6 :     ulong needs_2b  = (msg_sz>0x7F);
     199           6 :     buf[ buf_sz ]   = FD_LOG_COLLECTOR_PROTO_TAG;
     200           6 :     buf[ buf_sz+1 ] = (uchar)( (msg_sz&0x7F) | (needs_2b<<7) );
     201           6 :     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           6 :     ulong buf_start = buf_sz + 2 + needs_2b;
     205           6 :     ulong offset = buf_start;
     206             : 
     207           6 :     va_start(args, num_buffers);  // Restart argument list traversal
     208          18 :     for (int i = 0; i < num_buffers; i++) {
     209          12 :         char const *msg = va_arg( args, char const * );
     210          12 :         ulong msg_sz_part = va_arg( args, ulong );
     211          12 :         fd_memcpy( buf + offset, msg, msg_sz_part );
     212          12 :         offset += msg_sz_part;
     213          12 :     }
     214           6 :     va_end(args);
     215           6 :     log->buf_sz = (ushort)offset;
     216           6 :   }
     217           9 : }
     218             : 
     219           3 : #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           3 :                                            char const * fmt, ... ) {
     244           3 :   fd_log_collector_t * log = &ctx->txn_ctx->log_collector;
     245           3 :   if( FD_LIKELY( log->disabled ) ) {
     246           0 :     return;
     247           0 :   }
     248             : 
     249           3 :   uchar * buf    = log->buf;
     250           3 :   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           3 :   va_list ap;
     255           3 :   va_start( ap, fmt );
     256           3 :   int res = vsnprintf( (char *)(buf + buf_sz + 2), FD_LOG_COLLECTOR_PRINTF_MAX_1B, fmt, ap );
     257           3 :   va_end( ap );
     258             : 
     259             :   /* We use vsnprintf to protect against oob writes, however, it should
     260             :      never truncate.  If truncate happens, it means that we're using
     261             :      fd_log_collector_printf_dangerous_max_127(), incorrectly for
     262             :      example with a "%s" and an unbound variable (user input, var that's
     263             :      not null-terminated cstr, ...).
     264             :      We MUST only use fd_log_collector_printf_dangerous_max_127()
     265             :      as a convenience method, when we can guarantee that the total
     266             :      msg_sz is bound by FD_LOG_COLLECTOR_PRINTF_MAX_1B. */
     267           3 :   FD_TEST_CUSTOM( res>=0 && res<FD_LOG_COLLECTOR_PRINTF_MAX_1B,
     268           3 :     "A transaction log was truncated unexpectedly. Please report to developers." );
     269             : 
     270             :   /* Decide if we should include the log or truncate. */
     271           3 :   ulong msg_sz = (ulong)res;
     272           3 :   ulong bytes_written = fd_log_collector_check_and_truncate( log, msg_sz );
     273           3 :   if( FD_LIKELY( bytes_written < ULONG_MAX ) ) {
     274             :     /* Insert log: store tag + msg_sz (1 byte) and update buf_sz */
     275           3 :     log->log_sz = (ushort)bytes_written;
     276           3 :     buf[ buf_sz   ] = FD_LOG_COLLECTOR_PROTO_TAG;
     277           3 :     buf[ buf_sz+1 ] = (uchar)( msg_sz & 0x7F );
     278           3 :     log->buf_sz = (ushort)( buf_sz + msg_sz + 2 );
     279           3 :   }
     280           3 : }
     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
     317             :      never truncate.  If truncate happens, it means that we're using
     318             :      fd_log_collector_printf_dangerous_max_127(), incorrectly for
     319             :      example with a "%s" and an unbound variable (user input, var that's
     320             :      not null-terminated cstr, ...).
     321             :      We MUST only use fd_log_collector_printf_dangerous_max_128_to_2k()
     322             :      as a convenience method, when we can guarantee that the total
     323             :      msg_sz is 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           0 :                                              char const * fmt, ... ) {
     352           0 :   char msg[ 512 ];
     353             : 
     354           0 :   va_list ap;
     355           0 :   va_start( ap, fmt );
     356           0 :   int msg_sz = vsnprintf( msg, sizeof(msg), fmt, ap );
     357           0 :   va_end( ap );
     358             : 
     359           0 :   FD_TEST_CUSTOM( msg_sz>=0 && (ulong)msg_sz<sizeof(msg),
     360           0 :     "A transaction log was truncated unexpectedly. Please report to developers." );
     361             : 
     362           0 :   fd_log_collector_msg( ctx, msg, (ulong)msg_sz );
     363           0 : }
     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           0 : fd_log_collector_program_invoke( fd_exec_instr_ctx_t * ctx ) {
     386           0 :   if( FD_LIKELY( ctx->txn_ctx->log_collector.disabled ) ) {
     387           0 :     return;
     388           0 :   }
     389             : 
     390           0 :   fd_pubkey_t const * program_id_pubkey = &ctx->txn_ctx->account_keys[ ctx->instr->program_id ];
     391             :   /* Cache ctx->program_id_base58 */
     392           0 :   fd_base58_encode_32( program_id_pubkey->uc, NULL, ctx->program_id_base58 );
     393             :   /* Max msg_sz: 22 - 4 + 44 + 10 = 72 < 127 => we can use printf */
     394           0 :   fd_log_collector_printf_dangerous_max_127( ctx, "Program %s invoke [%u]", ctx->program_id_base58, ctx->txn_ctx->instr_stack_sz );
     395           0 : }
     396             : 
     397             : /* fd_log_collector_program_log logs:
     398             :      "Program log: <msg>"
     399             : 
     400             :    msg must be a valid utf8 string, it's responsibility of the caller to
     401             :    validate that.  This is the implementation underlying the _sol_log()
     402             :    syscall. */
     403             : static inline void
     404           9 : fd_log_collector_program_log( fd_exec_instr_ctx_t * ctx, char const * msg, ulong msg_sz ) {
     405           9 :   fd_log_collector_msg_many( ctx, 2, "Program log: ", 13UL, msg, msg_sz );
     406           9 : }
     407             : 
     408             : /* fd_log_collector_program_return logs:
     409             :      "Program return: <ProgramIdBase58> <dataAsBase64>"
     410             : 
     411             :    Since return data is at most 1024 bytes, it's base64 representation is
     412             :    at most 1368 bytes and msg_sz is known in advance, thus we can use
     413             :    fd_log_collector_printf_*.
     414             : 
     415             :    TODO: implement based on fd_log_collector_msg_many(). */
     416             : static inline void
     417           0 : fd_log_collector_program_return( fd_exec_instr_ctx_t * ctx ) {
     418           0 :   if( FD_LIKELY( ctx->txn_ctx->log_collector.disabled ) ) {
     419           0 :     return;
     420           0 :   }
     421             : 
     422             :   /* ctx->txn_ctx->return_data is 1024 bytes max, so its base64 repr
     423             :      is at most (1024+2)/3*4 bytes, plus we use 1 byte for \0. */
     424           0 :   char return_base64[ (sizeof(ctx->txn_ctx->return_data.data)+2)/3*4+1 ];
     425           0 :   ulong sz = fd_base64_encode( return_base64, ctx->txn_ctx->return_data.data, ctx->txn_ctx->return_data.len );
     426           0 :   return_base64[ sz ] = 0;
     427             :   /* Max msg_sz: 21 - 4 + 44 + 1368 = 1429 < 1500 => we can use printf, but have to handle sz */
     428           0 :   ulong msg_sz = 17 + strlen(ctx->program_id_base58) + sz;
     429           0 :   if( msg_sz<=127 ) {
     430           0 :     fd_log_collector_printf_dangerous_max_127( ctx, "Program return: %s %s", ctx->program_id_base58, return_base64 );
     431           0 :   } else {
     432           0 :     fd_log_collector_printf_dangerous_128_to_2k( ctx, "Program return: %s %s", ctx->program_id_base58, return_base64 );
     433           0 :   }
     434           0 : }
     435             : 
     436             : /* fd_log_collector_program_success logs:
     437             :      "Program <ProgramIdBase58> success" */
     438             : static inline void
     439           0 : fd_log_collector_program_success( fd_exec_instr_ctx_t * ctx ) {
     440             :   /* Max msg_sz: 18 - 2 + 44 = 60 < 127 => we can use printf */
     441           0 :   fd_log_collector_printf_dangerous_max_127( ctx, "Program %s success", ctx->program_id_base58 );
     442           0 : }
     443             : 
     444             : /* fd_log_collector_program_success logs:
     445             :      "Program <ProgramIdBase58> failed: <err>"
     446             : 
     447             :    This function handles the logic to log the correct msg, based
     448             :    on the type of error (InstructionError, SyscallError...).
     449             :    https://github.com/anza-xyz/agave/blob/v2.0.6/program-runtime/src/invoke_context.rs#L535-L549
     450             : 
     451             :    The error msg is obtained by external functions, e.g. fd_vm_syscall_strerror(),
     452             :    and can be either a valid msg or an empty string.  Empty string represents
     453             :    special handling of the error log, for example the syscall panic logs directly
     454             :    the result, and therefore can be skipped at this stage. */
     455             : static inline void
     456           0 : fd_log_collector_program_failure( fd_exec_instr_ctx_t * ctx ) {
     457           0 :   if( FD_LIKELY( ctx->txn_ctx->log_collector.disabled ) ) {
     458           0 :     return;
     459           0 :   }
     460             : 
     461           0 :   extern char const * fd_vm_ebpf_strerror( int err );
     462           0 :   extern char const * fd_vm_syscall_strerror( int err );
     463           0 :   extern char const * fd_executor_instr_strerror( int err );
     464             : 
     465           0 :   char custom_err[33] = { 0 };
     466           0 :   const char * err = custom_err;
     467           0 :   const fd_exec_txn_ctx_t * txn_ctx = ctx->txn_ctx;
     468           0 :   if( FD_UNLIKELY( txn_ctx->exec_err_kind==FD_EXECUTOR_ERR_KIND_INSTR &&
     469           0 :                    txn_ctx->exec_err==FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR ) ) {
     470             :     /* Max msg_sz = 32 <= 66 */
     471           0 :     snprintf( custom_err, sizeof(custom_err), "custom program error: 0x%x", txn_ctx->custom_err );
     472           0 :   } else if( txn_ctx->exec_err ) {
     473           0 :     switch( txn_ctx->exec_err_kind ) {
     474           0 :       case FD_EXECUTOR_ERR_KIND_SYSCALL:
     475           0 :         err = fd_vm_syscall_strerror( txn_ctx->exec_err );
     476           0 :         break;
     477           0 :       case FD_EXECUTOR_ERR_KIND_INSTR:
     478           0 :         err = fd_executor_instr_strerror( txn_ctx->exec_err );
     479           0 :         break;
     480           0 :       default:
     481           0 :         err = fd_vm_ebpf_strerror( txn_ctx->exec_err );
     482           0 :     }
     483           0 :   }
     484             : 
     485             :   /* Skip empty string, this means that the msg has already been logged. */
     486           0 :   if( FD_LIKELY( err[0] ) ) {
     487           0 :     char err_prefix[ 17+FD_BASE58_ENCODED_32_SZ ]; // 17==strlen("Program  failed: ")
     488           0 :     int err_prefix_len = sprintf( err_prefix, "Program %s failed: ", ctx->program_id_base58 );
     489           0 :     if( err_prefix_len > 0 ) {
     490             :       /* Equivalent to: "Program %s failed: %s" */
     491           0 :       fd_log_collector_msg_many( ctx, 2, err_prefix, (ulong)err_prefix_len, err, (ulong)strlen(err) );
     492           0 :     }
     493           0 :   }
     494           0 : }
     495             : 
     496             : /* fd_log_collector_program_consumed logs:
     497             :      "Program <ProgramIdBase58> consumed <consumed> of <tota> compute units" */
     498             : static inline void
     499           0 : fd_log_collector_program_consumed( fd_exec_instr_ctx_t * ctx, ulong consumed, ulong total ) {
     500             :   /* Max msg_sz: 44 - 8 + 44 + 20 + 20 = 120 < 127 => we can use printf */
     501           0 :   fd_log_collector_printf_dangerous_max_127( ctx, "Program %s consumed %lu of %lu compute units", ctx->program_id_base58, consumed, total );
     502           0 : }
     503             : 
     504             : /* DEBUG
     505             :    Only used for testing (inefficient but ok). */
     506             : 
     507             : static inline ushort
     508          57 : fd_log_collector_debug_get_msg_sz( uchar const ** buf ) {
     509          57 :   uchar msg0 = (*buf)[1];
     510          57 :   uchar msg1 = (*buf)[2]; /* This is never oob */
     511          57 :   int needs_2b = (msg0>0x7F);
     512          57 :   ushort msg_sz = fd_ushort_if( needs_2b, (ushort)(((ushort)(msg1) << 7)|(msg0 & 0x7F)), (ushort)msg0 );
     513          57 :   *buf += 2 + needs_2b;
     514          57 :   return msg_sz;
     515          57 : }
     516             : 
     517             : static inline ulong
     518          30 : fd_log_collector_debug_len( fd_log_collector_t const * log ) {
     519          30 :   ulong len = 0;
     520          63 :   for( uchar const * cur = log->buf; cur < log->buf + log->buf_sz; ) {
     521          33 :     ushort cur_sz = fd_log_collector_debug_get_msg_sz( &cur );
     522          33 :     cur += cur_sz;
     523          33 :     ++len;
     524          33 :   }
     525          30 :   return len;
     526          30 : }
     527             : 
     528             : static inline uchar const *
     529             : fd_log_collector_debug_get( fd_log_collector_t const * log,
     530             :                             ulong                      log_num,
     531             :                             uchar const **             msg,
     532          15 :                             ulong *                    msg_sz ) {
     533          15 :   uchar const * cur = log->buf;
     534          15 :   ushort cur_sz = 0;
     535             : 
     536          15 :   cur_sz = fd_log_collector_debug_get_msg_sz( &cur );
     537          24 :   while( log_num>0 ) {
     538           9 :     cur += cur_sz;
     539           9 :     cur_sz = fd_log_collector_debug_get_msg_sz( &cur );
     540           9 :     --log_num;
     541           9 :   }
     542          15 :   if( msg )    *msg    = cur;
     543          15 :   if( msg_sz ) *msg_sz = cur_sz;
     544          15 :   return cur;
     545          15 : }
     546             : 
     547             : static inline ulong
     548             : fd_log_collector_debug_sprintf( fd_log_collector_t const * log,
     549             :                                 char *                     out,
     550           0 :                                 int                        filter_zero ) {
     551           0 :   ulong out_sz = 0;
     552             : 
     553           0 :   ulong pos = 0;
     554           0 :   uchar const * buf = log->buf;
     555           0 :   while( pos < log->buf_sz ) {
     556             :     /* Read cur string sz */
     557           0 :     ushort cur_sz = fd_log_collector_debug_get_msg_sz( &buf );
     558             : 
     559             :     /* Copy string and add \n.
     560             :        Slow version of memcpy that skips \0, because a \0 can be in logs.
     561             :        Equivalent to:
     562             :        fd_memcpy( out + out_sz, buf, cur_sz ); out_sz += cur_sz; */
     563           0 :     if( filter_zero ) {
     564           0 :       for( ulong i=0; i<cur_sz; i++ ) {
     565           0 :         if( buf[i] ) {
     566           0 :           out[ out_sz++ ] = (char)buf[i];
     567           0 :         }
     568           0 :       }
     569           0 :     } else {
     570           0 :       fd_memcpy( out+out_sz, buf, cur_sz );
     571           0 :       out_sz += cur_sz;
     572           0 :     }
     573           0 :     out[ out_sz++ ] = '\n';
     574             : 
     575             :     /* Move to next str */
     576           0 :     buf += cur_sz;
     577           0 :     pos = (ulong)(buf - log->buf);
     578           0 :   }
     579             : 
     580             :   /* Remove the last \n, or return empty cstr */
     581           0 :   out_sz = out_sz ? out_sz-1 : 0;
     582           0 :   out[ out_sz ] = '\0';
     583           0 :   return out_sz;
     584           0 : }
     585             : 
     586             : static inline void
     587           0 : fd_log_collector_private_debug( fd_log_collector_t const * log ) {
     588           0 :   char out[FD_LOG_COLLECTOR_MAX + FD_LOG_COLLECTOR_EXTRA];
     589           0 :   fd_log_collector_debug_sprintf( log, out, 1 );
     590           0 :   FD_LOG_WARNING(( "\n-----\n%s\n-----", out ));
     591           0 : }
     592             : 
     593             : FD_PROTOTYPES_END
     594             : 
     595             : #endif /* HEADER_fd_src_flamenco_log_collector_fd_log_collector_h */

Generated by: LCOV version 1.14