LCOV - code coverage report
Current view: top level - bpfilter/cgen - printer.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 86.2 % 116 100
Test Date: 2025-11-24 12:34:34 Functions: 92.9 % 14 13
Branches: 48.2 % 110 53

             Branch data     Line data    Source code
       1                 :             : // SPDX-License-Identifier: GPL-2.0-only
       2                 :             : /*
       3                 :             :  * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
       4                 :             :  */
       5                 :             : 
       6                 :             : #include "cgen/printer.h"
       7                 :             : 
       8                 :             : #include <errno.h>
       9                 :             : #include <stdlib.h>
      10                 :             : #include <string.h>
      11                 :             : 
      12                 :             : #include <bpfilter/dump.h>
      13                 :             : #include <bpfilter/helper.h>
      14                 :             : #include <bpfilter/list.h>
      15                 :             : #include <bpfilter/logger.h>
      16                 :             : #include <bpfilter/pack.h>
      17                 :             : 
      18                 :             : /**
      19                 :             :  * @struct bf_printer_msg
      20                 :             :  *
      21                 :             :  * Represents a message to be printed by a generated BPF program.
      22                 :             :  */
      23                 :             : struct bf_printer_msg
      24                 :             : {
      25                 :             :     /// Offset of the message, in the concatenated messages string.
      26                 :             :     size_t offset;
      27                 :             :     /// Length of the message, including the nul termination character.
      28                 :             :     size_t len;
      29                 :             :     /// Message.
      30                 :             :     const char *str;
      31                 :             : };
      32                 :             : 
      33                 :             : /**
      34                 :             :  * @struct bf_printer
      35                 :             :  *
      36                 :             :  * The printer context. It stores all the messages to be printed by any of the
      37                 :             :  * generated BPF program, and the runtime data required to maintain the
      38                 :             :  * log messages BPF map.
      39                 :             :  */
      40                 :             : struct bf_printer
      41                 :             : {
      42                 :             :     /// List of messages.
      43                 :             :     bf_list msgs;
      44                 :             : };
      45                 :             : 
      46                 :             : #define _free_bf_printer_msg_ __attribute__((__cleanup__(_bf_printer_msg_free)))
      47                 :             : 
      48                 :             : static void _bf_printer_msg_free(struct bf_printer_msg **msg);
      49                 :             : 
      50                 :             : /**
      51                 :             :  * Allocate and initialise a new printer message.
      52                 :             :  *
      53                 :             :  * @param msg On success, points to the newly allocated and initialised
      54                 :             :  *        printer message. Can't be NULL.
      55                 :             :  * @return 0 on success, or negative errno value on error.
      56                 :             :  */
      57                 :          86 : static int _bf_printer_msg_new(struct bf_printer_msg **msg)
      58                 :             : {
      59                 :          86 :     _free_bf_printer_msg_ struct bf_printer_msg *_msg = NULL;
      60                 :             : 
      61                 :             :     bf_assert(msg);
      62                 :             : 
      63                 :          86 :     _msg = calloc(1, sizeof(*_msg));
      64         [ +  - ]:          86 :     if (!_msg)
      65                 :             :         return -ENOMEM;
      66                 :             : 
      67                 :          86 :     *msg = TAKE_PTR(_msg);
      68                 :             : 
      69                 :          86 :     return 0;
      70                 :             : }
      71                 :             : 
      72                 :             : /**
      73                 :             :  * @brief Allocate and initialize a new printer message from serialized data.
      74                 :             :  *
      75                 :             :  * @param msg Printer message object to allocate and initialize from the
      76                 :             :  *        serialized data. The caller will own the object. On failure, `*msg`
      77                 :             :  *        is unchanged. Can't be NULL.
      78                 :             :  * @param node Node containing the serialized message.
      79                 :             :  * @return 0 on success, or a negative errno value on failure.
      80                 :             :  */
      81                 :           2 : static int _bf_printer_msg_new_from_pack(struct bf_printer_msg **msg,
      82                 :             :                                          bf_rpack_node_t node)
      83                 :             : {
      84                 :           2 :     _free_bf_printer_msg_ struct bf_printer_msg *_msg = NULL;
      85                 :             :     int r;
      86                 :             : 
      87                 :             :     bf_assert(msg);
      88                 :             : 
      89                 :           2 :     r = _bf_printer_msg_new(&_msg);
      90         [ -  + ]:           2 :     if (r)
      91         [ #  # ]:           0 :         return bf_err_r(r, "failed to create bf_printer_msg from pack");
      92                 :             : 
      93                 :           2 :     r = bf_rpack_kv_u64(node, "offset", &_msg->offset);
      94         [ -  + ]:           2 :     if (r)
      95         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_printer_msg.offset");
      96                 :             : 
      97                 :           2 :     r = bf_rpack_kv_u64(node, "len", &_msg->len);
      98         [ -  + ]:           2 :     if (r)
      99         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_printer_msg.len");
     100                 :             : 
     101                 :           2 :     r = bf_rpack_kv_str(node, "str", (char **)&_msg->str);
     102         [ -  + ]:           2 :     if (r)
     103         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_printer_msg.str");
     104                 :             : 
     105                 :           2 :     *msg = TAKE_PTR(_msg);
     106                 :             : 
     107                 :           2 :     return 0;
     108                 :             : }
     109                 :             : 
     110                 :             : /**
     111                 :             :  * Deinitialise and deallocate a printer message.
     112                 :             :  *
     113                 :             :  * @param msg Printer message. Can't be NULL.
     114                 :             :  */
     115                 :         260 : static void _bf_printer_msg_free(struct bf_printer_msg **msg)
     116                 :             : {
     117                 :             :     bf_assert(msg);
     118                 :             : 
     119         [ +  + ]:         260 :     if (!*msg)
     120                 :             :         return;
     121                 :             : 
     122                 :             :     // Compiler will complain if str is const
     123                 :          86 :     free((char *)(*msg)->str);
     124                 :             : 
     125                 :          86 :     free(*msg);
     126                 :          86 :     *msg = NULL;
     127                 :             : }
     128                 :             : 
     129                 :             : /**
     130                 :             :  * @brief Serialize a printer message.
     131                 :             :  *
     132                 :             :  * @param msg Printer message to serialize. Can't be NULL.
     133                 :             :  * @param pack `bf_wpack_t` object to serialize the printer message into.
     134                 :             :  *        Can't be NULL.
     135                 :             :  * @return 0 on success, or a negative error value on failure.
     136                 :             :  */
     137                 :         240 : static int _bf_printer_msg_pack(const struct bf_printer_msg *msg,
     138                 :             :                                 bf_wpack_t *pack)
     139                 :             : {
     140                 :             :     bf_assert(msg);
     141                 :             :     bf_assert(pack);
     142                 :             : 
     143                 :         240 :     bf_wpack_kv_u64(pack, "offset", msg->offset);
     144                 :         240 :     bf_wpack_kv_u64(pack, "len", msg->len);
     145                 :         240 :     bf_wpack_kv_str(pack, "str", msg->str);
     146                 :             : 
     147         [ -  + ]:         240 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
     148                 :             : }
     149                 :             : 
     150                 :           2 : static void _bf_printer_msg_dump(const struct bf_printer_msg *msg,
     151                 :             :                                  prefix_t *prefix)
     152                 :             : {
     153                 :             :     bf_assert(msg);
     154                 :             :     bf_assert(prefix);
     155                 :             : 
     156         [ +  - ]:           2 :     DUMP(prefix, "struct bf_printer_msg at %p", msg);
     157                 :             : 
     158                 :           2 :     bf_dump_prefix_push(prefix);
     159         [ +  - ]:           2 :     DUMP(prefix, "offset: %lu", msg->offset);
     160         [ +  - ]:           2 :     DUMP(prefix, "len: %lu", msg->len);
     161         [ +  - ]:           2 :     DUMP(bf_dump_prefix_last(prefix), "str: '%s'", msg->str);
     162                 :           2 :     bf_dump_prefix_pop(prefix);
     163                 :           2 : }
     164                 :             : 
     165                 :          84 : size_t bf_printer_msg_offset(const struct bf_printer_msg *msg)
     166                 :             : {
     167                 :             :     bf_assert(msg);
     168                 :          84 :     return msg->offset;
     169                 :             : }
     170                 :             : 
     171                 :           0 : size_t bf_printer_msg_len(const struct bf_printer_msg *msg)
     172                 :             : {
     173                 :             :     bf_assert(msg);
     174                 :           0 :     return msg->len;
     175                 :             : }
     176                 :             : 
     177                 :          64 : int bf_printer_new(struct bf_printer **printer)
     178                 :             : {
     179                 :          64 :     _free_bf_printer_ struct bf_printer *_printer = NULL;
     180                 :             : 
     181                 :             :     bf_assert(printer);
     182                 :             : 
     183                 :          64 :     _printer = malloc(sizeof(*_printer));
     184         [ +  - ]:          64 :     if (!_printer)
     185                 :             :         return -ENOMEM;
     186                 :             : 
     187                 :          64 :     _printer->msgs =
     188                 :             :         bf_list_default(_bf_printer_msg_free, _bf_printer_msg_pack);
     189                 :             : 
     190                 :          64 :     *printer = TAKE_PTR(_printer);
     191                 :             : 
     192                 :          64 :     return 0;
     193                 :             : }
     194                 :             : 
     195                 :           2 : int bf_printer_new_from_pack(struct bf_printer **printer, bf_rpack_node_t node)
     196                 :             : {
     197                 :           2 :     _free_bf_printer_ struct bf_printer *_printer = NULL;
     198                 :             :     bf_rpack_node_t child, m_node;
     199                 :             :     int r;
     200                 :             : 
     201                 :             :     bf_assert(printer);
     202                 :             : 
     203                 :           2 :     r = bf_printer_new(&_printer);
     204         [ -  + ]:           2 :     if (r)
     205         [ #  # ]:           0 :         return bf_err_r(r, "failed to create a bf_printer from pack");
     206                 :             : 
     207                 :           2 :     r = bf_rpack_kv_array(node, "msgs", &child);
     208         [ -  + ]:           2 :     if (r)
     209         [ #  # ]:           0 :         return bf_rpack_key_err(r, "bf_printer.msgs");
     210   [ +  -  -  +  :           8 :     bf_rpack_array_foreach (child, m_node) {
                   +  + ]
     211                 :           4 :         _free_bf_printer_msg_ struct bf_printer_msg *msg = NULL;
     212                 :             : 
     213   [ +  -  +  -  :           2 :         r = bf_list_emplace(&_printer->msgs, _bf_printer_msg_new_from_pack, msg,
             -  -  -  - ]
     214                 :             :                             m_node);
     215                 :             :         if (r) {
     216         [ #  # ]:           0 :             return bf_err_r(
     217                 :             :                 r, "failed to unpack bf_printer_msg into bf_printer.msgs");
     218                 :             :         }
     219                 :             :     }
     220                 :             : 
     221                 :           2 :     *printer = TAKE_PTR(_printer);
     222                 :             : 
     223                 :           2 :     return 0;
     224                 :             : }
     225                 :             : 
     226                 :         130 : void bf_printer_free(struct bf_printer **printer)
     227                 :             : {
     228                 :             :     bf_assert(printer);
     229                 :             : 
     230         [ +  + ]:         130 :     if (!*printer)
     231                 :             :         return;
     232                 :             : 
     233                 :          64 :     bf_list_clean(&(*printer)->msgs);
     234                 :             : 
     235                 :          64 :     free(*printer);
     236                 :          64 :     *printer = NULL;
     237                 :             : }
     238                 :             : 
     239                 :         164 : int bf_printer_pack(const struct bf_printer *printer, bf_wpack_t *pack)
     240                 :             : {
     241                 :             :     bf_assert(printer);
     242                 :             :     bf_assert(pack);
     243                 :             : 
     244                 :         164 :     bf_wpack_kv_list(pack, "msgs", &printer->msgs);
     245                 :             : 
     246         [ -  + ]:         164 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
     247                 :             : }
     248                 :             : 
     249                 :           2 : void bf_printer_dump(const struct bf_printer *printer, prefix_t *prefix)
     250                 :             : {
     251                 :             :     bf_assert(printer);
     252                 :             :     bf_assert(prefix);
     253                 :             : 
     254         [ +  - ]:           2 :     DUMP(prefix, "struct bf_printer at %p", printer);
     255                 :             : 
     256                 :           2 :     bf_dump_prefix_push(prefix);
     257         [ +  - ]:           2 :     DUMP(prefix, "msgs: bf_list<bf_printer_msg>[%lu]",
     258                 :             :          bf_list_size(&printer->msgs));
     259                 :           2 :     bf_dump_prefix_push(prefix);
     260   [ +  -  -  +  :           8 :     bf_list_foreach (&printer->msgs, msg_node) {
                   +  + ]
     261                 :             :         struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
     262                 :             : 
     263         [ +  - ]:           2 :         if (bf_list_is_tail(&printer->msgs, msg_node))
     264                 :           2 :             bf_dump_prefix_last(prefix);
     265                 :             : 
     266                 :           2 :         _bf_printer_msg_dump(msg, prefix);
     267                 :             :     }
     268                 :           2 :     bf_dump_prefix_pop(prefix);
     269                 :             : 
     270                 :           2 :     bf_dump_prefix_pop(prefix);
     271                 :           2 : }
     272                 :             : 
     273                 :             : /**
     274                 :             :  * Get the total size of the concatenated messages.
     275                 :             :  *
     276                 :             :  * @param printer Printer context. Can't be NULL.
     277                 :             :  * @return Total size of the concatenated messages.
     278                 :             :  */
     279                 :             : static size_t _bf_printer_total_size(const struct bf_printer *printer)
     280                 :             : {
     281                 :             :     bf_list_node *last_msg_node;
     282                 :             :     struct bf_printer_msg *last_msg;
     283                 :             : 
     284                 :             :     bf_assert(printer);
     285                 :             : 
     286   [ +  -  +  + ]:         144 :     if (!(last_msg_node = bf_list_get_tail(&printer->msgs)))
     287                 :             :         return 0;
     288                 :             : 
     289                 :             :     last_msg = bf_list_node_get_data(last_msg_node);
     290                 :             : 
     291                 :          24 :     return last_msg->offset + last_msg->len;
     292                 :             : }
     293                 :             : 
     294                 :          84 : const struct bf_printer_msg *bf_printer_add_msg(struct bf_printer *printer,
     295                 :             :                                                 const char *str)
     296                 :             : {
     297                 :          84 :     _free_bf_printer_msg_ struct bf_printer_msg *msg = NULL;
     298                 :             :     int r;
     299                 :             : 
     300                 :             :     bf_assert(printer);
     301                 :             :     bf_assert(str);
     302                 :             : 
     303                 :             :     // Check if an identical message is already stored in the context.
     304   [ +  +  -  +  :         192 :     bf_list_foreach (&printer->msgs, msg_node) {
                   +  + ]
     305                 :             :         struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
     306                 :             : 
     307         [ +  - ]:          24 :         if (bf_streq(msg->str, str))
     308                 :             :             return msg;
     309                 :             :     }
     310                 :             : 
     311                 :             :     // Otherwise, create a new message.
     312                 :          84 :     r = _bf_printer_msg_new(&msg);
     313         [ -  + ]:          84 :     if (r) {
     314         [ #  # ]:           0 :         bf_err("failed to create a new bf_printer_msg object");
     315                 :           0 :         return NULL;
     316                 :             :     }
     317                 :             : 
     318         [ +  + ]:          84 :     msg->len = strlen(str) + 1;
     319                 :             :     // Next expected offset is equal to the current total size of the
     320                 :             :     // concatenated string
     321                 :          84 :     msg->offset = _bf_printer_total_size(printer);
     322                 :          84 :     msg->str = strdup(str);
     323         [ -  + ]:          84 :     if (!msg->str)
     324                 :             :         return NULL;
     325                 :             : 
     326                 :          84 :     r = bf_list_add_tail(&printer->msgs, msg);
     327         [ -  + ]:          84 :     if (r) {
     328         [ #  # ]:           0 :         bf_err("failed to add a new printer message to the printer context");
     329                 :           0 :         return NULL;
     330                 :             :     }
     331                 :             : 
     332                 :          84 :     return TAKE_PTR(msg);
     333                 :             : }
     334                 :             : 
     335         [ +  - ]:          60 : int bf_printer_assemble(const struct bf_printer *printer, void **str,
     336                 :             :                         size_t *str_len)
     337                 :             : {
     338                 :             :     _cleanup_free_ char *_str = NULL;
     339                 :             :     size_t _str_len;
     340                 :             : 
     341                 :             :     bf_assert(printer);
     342                 :             :     bf_assert(str);
     343                 :             :     bf_assert(str_len);
     344                 :             : 
     345                 :             :     _str_len = _bf_printer_total_size(printer);
     346                 :             : 
     347                 :             :     // If the printer doesn't contain any message, the string should only
     348                 :             :     // contain \0.
     349         [ -  + ]:          60 :     if (_str_len == 0) {
     350                 :           0 :         _str = malloc(1);
     351         [ #  # ]:           0 :         if (!_str)
     352                 :             :             return -ENOMEM;
     353                 :             : 
     354                 :           0 :         *_str = '\0';
     355                 :             :         _str_len = 1;
     356                 :             :     } else {
     357                 :             :         _str_len = _bf_printer_total_size(printer);
     358                 :          60 :         _str = malloc(_str_len);
     359         [ -  + ]:          60 :         if (!_str)
     360                 :             :             return -ENOMEM;
     361                 :             : 
     362   [ +  -  +  + ]:         228 :         bf_list_foreach (&printer->msgs, msg_node) {
     363                 :             :             struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
     364         [ +  + ]:          84 :             memcpy(_str + msg->offset, msg->str, msg->len);
     365                 :             :         }
     366                 :             :     }
     367                 :             : 
     368                 :          60 :     *str = TAKE_PTR(_str);
     369                 :          60 :     *str_len = _str_len;
     370                 :             : 
     371                 :          60 :     return 0;
     372                 :             : }
        

Generated by: LCOV version 2.0-1