LCOV - code coverage report
Current view: top level - bpfilter/cgen - printer.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 58.6 % 145 85
Test Date: 2025-09-30 16:37:25 Functions: 66.7 % 15 10

            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           20 : static int _bf_printer_msg_new(struct bf_printer_msg **msg)
      58              : {
      59           19 :     _free_bf_printer_msg_ struct bf_printer_msg *_msg = NULL;
      60              : 
      61           20 :     bf_assert(msg);
      62              : 
      63           19 :     _msg = calloc(1, sizeof(*_msg));
      64           19 :     if (!_msg)
      65              :         return -ENOMEM;
      66              : 
      67           18 :     *msg = TAKE_PTR(_msg);
      68              : 
      69           18 :     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            8 : static int _bf_printer_msg_new_from_pack(struct bf_printer_msg **msg,
      82              :                                          bf_rpack_node_t node)
      83              : {
      84            8 :     _free_bf_printer_msg_ struct bf_printer_msg *_msg = NULL;
      85              :     int r;
      86              : 
      87            8 :     bf_assert(msg);
      88              : 
      89            8 :     r = _bf_printer_msg_new(&_msg);
      90            8 :     if (r)
      91            0 :         return bf_err_r(r, "failed to create bf_printer_msg from pack");
      92              : 
      93            8 :     r = bf_rpack_kv_u64(node, "offset", &_msg->offset);
      94            8 :     if (r)
      95            0 :         return bf_rpack_key_err(r, "bf_printer_msg.offset");
      96              : 
      97            8 :     r = bf_rpack_kv_u64(node, "len", &_msg->len);
      98            8 :     if (r)
      99            0 :         return bf_rpack_key_err(r, "bf_printer_msg.len");
     100              : 
     101            8 :     r = bf_rpack_kv_str(node, "str", (char **)&_msg->str);
     102            8 :     if (r)
     103            0 :         return bf_rpack_key_err(r, "bf_printer_msg.str");
     104              : 
     105            8 :     *msg = TAKE_PTR(_msg);
     106              : 
     107            8 :     return 0;
     108              : }
     109              : 
     110              : /**
     111              :  * Deinitialise and deallocate a printer message.
     112              :  *
     113              :  * @param msg Printer message. Can't be NULL.
     114              :  */
     115           64 : static void _bf_printer_msg_free(struct bf_printer_msg **msg)
     116              : {
     117           64 :     bf_assert(msg);
     118              : 
     119           64 :     if (!*msg)
     120              :         return;
     121              : 
     122              :     // Compiler will complain if str is const
     123           18 :     free((char *)(*msg)->str);
     124              : 
     125           18 :     free(*msg);
     126           18 :     *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            8 : static int _bf_printer_msg_pack(const struct bf_printer_msg *msg,
     138              :                                 bf_wpack_t *pack)
     139              : {
     140            8 :     bf_assert(msg);
     141            8 :     bf_assert(pack);
     142              : 
     143            8 :     bf_wpack_kv_u64(pack, "offset", msg->offset);
     144            8 :     bf_wpack_kv_u64(pack, "len", msg->len);
     145            8 :     bf_wpack_kv_str(pack, "str", msg->str);
     146              : 
     147            8 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
     148              : }
     149              : 
     150            0 : static void _bf_printer_msg_dump(const struct bf_printer_msg *msg,
     151              :                                  prefix_t *prefix)
     152              : {
     153            0 :     bf_assert(msg);
     154            0 :     bf_assert(prefix);
     155              : 
     156            0 :     DUMP(prefix, "struct bf_printer_msg at %p", msg);
     157              : 
     158            0 :     bf_dump_prefix_push(prefix);
     159            0 :     DUMP(prefix, "offset: %lu", msg->offset);
     160            0 :     DUMP(prefix, "len: %lu", msg->len);
     161            0 :     DUMP(bf_dump_prefix_last(prefix), "str: '%s'", msg->str);
     162            0 :     bf_dump_prefix_pop(prefix);
     163            0 : }
     164              : 
     165            0 : size_t bf_printer_msg_offset(const struct bf_printer_msg *msg)
     166              : {
     167            0 :     bf_assert(msg);
     168            0 :     return msg->offset;
     169              : }
     170              : 
     171            0 : size_t bf_printer_msg_len(const struct bf_printer_msg *msg)
     172              : {
     173            0 :     bf_assert(msg);
     174            0 :     return msg->len;
     175              : }
     176              : 
     177            9 : int bf_printer_new(struct bf_printer **printer)
     178              : {
     179            8 :     _free_bf_printer_ struct bf_printer *_printer = NULL;
     180              : 
     181            9 :     bf_assert(printer);
     182              : 
     183            8 :     _printer = malloc(sizeof(*_printer));
     184            8 :     if (!_printer)
     185              :         return -ENOMEM;
     186              : 
     187            7 :     _printer->msgs =
     188              :         bf_list_default(_bf_printer_msg_free, _bf_printer_msg_pack);
     189              : 
     190            7 :     *printer = TAKE_PTR(_printer);
     191              : 
     192            7 :     return 0;
     193              : }
     194              : 
     195            1 : int bf_printer_new_from_pack(struct bf_printer **printer, bf_rpack_node_t node)
     196              : {
     197            1 :     _free_bf_printer_ struct bf_printer *_printer = NULL;
     198              :     bf_rpack_node_t child, m_node;
     199              :     int r;
     200              : 
     201            1 :     bf_assert(printer);
     202              : 
     203            1 :     r = bf_printer_new(&_printer);
     204            1 :     if (r)
     205            0 :         return bf_err_r(r, "failed to create a bf_printer from pack");
     206              : 
     207            1 :     r = bf_rpack_kv_array(node, "msgs", &child);
     208            1 :     if (r)
     209            0 :         return bf_rpack_key_err(r, "bf_printer.msgs");
     210           18 :     bf_rpack_array_foreach (child, m_node) {
     211           16 :         _free_bf_printer_msg_ struct bf_printer_msg *msg = NULL;
     212              : 
     213            8 :         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            1 :     *printer = TAKE_PTR(_printer);
     222              : 
     223            1 :     return 0;
     224              : }
     225              : 
     226           18 : void bf_printer_free(struct bf_printer **printer)
     227              : {
     228           18 :     bf_assert(printer);
     229              : 
     230           18 :     if (!*printer)
     231              :         return;
     232              : 
     233            7 :     bf_list_clean(&(*printer)->msgs);
     234              : 
     235            7 :     free(*printer);
     236            7 :     *printer = NULL;
     237              : }
     238              : 
     239            3 : int bf_printer_pack(const struct bf_printer *printer, bf_wpack_t *pack)
     240              : {
     241            3 :     bf_assert(printer);
     242            2 :     bf_assert(pack);
     243              : 
     244            1 :     bf_wpack_kv_list(pack, "msgs", &printer->msgs);
     245              : 
     246            1 :     return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
     247              : }
     248              : 
     249            0 : void bf_printer_dump(const struct bf_printer *printer, prefix_t *prefix)
     250              : {
     251            0 :     bf_assert(printer);
     252            0 :     bf_assert(prefix);
     253              : 
     254            0 :     DUMP(prefix, "struct bf_printer at %p", printer);
     255              : 
     256            0 :     bf_dump_prefix_push(prefix);
     257            0 :     DUMP(prefix, "msgs: bf_list<bf_printer_msg>[%lu]",
     258              :          bf_list_size(&printer->msgs));
     259            0 :     bf_dump_prefix_push(prefix);
     260            0 :     bf_list_foreach (&printer->msgs, msg_node) {
     261            0 :         struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
     262              : 
     263            0 :         if (bf_list_is_tail(&printer->msgs, msg_node))
     264            0 :             bf_dump_prefix_last(prefix);
     265              : 
     266            0 :         _bf_printer_msg_dump(msg, prefix);
     267              :     }
     268            0 :     bf_dump_prefix_pop(prefix);
     269              : 
     270            0 :     bf_dump_prefix_pop(prefix);
     271            0 : }
     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            8 : 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            8 :     bf_assert(printer);
     285              : 
     286            8 :     if (!(last_msg_node = bf_list_get_tail(&printer->msgs)))
     287              :         return 0;
     288              : 
     289            7 :     last_msg = bf_list_node_get_data(last_msg_node);
     290              : 
     291            7 :     return last_msg->offset + last_msg->len;
     292              : }
     293              : 
     294           10 : const struct bf_printer_msg *bf_printer_add_msg(struct bf_printer *printer,
     295              :                                                 const char *str)
     296              : {
     297           10 :     _free_bf_printer_msg_ struct bf_printer_msg *msg = NULL;
     298              :     int r;
     299              : 
     300           10 :     bf_assert(printer);
     301           10 :     bf_assert(str);
     302              : 
     303              :     // Check if an identical message is already stored in the context.
     304           78 :     bf_list_foreach (&printer->msgs, msg_node) {
     305           31 :         struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
     306              : 
     307           31 :         if (bf_streq(msg->str, str))
     308              :             return msg;
     309              :     }
     310              : 
     311              :     // Otherwise, create a new message.
     312            8 :     r = _bf_printer_msg_new(&msg);
     313            8 :     if (r) {
     314            0 :         bf_err("failed to create a new bf_printer_msg object");
     315            0 :         return NULL;
     316              :     }
     317              : 
     318            8 :     msg->len = strlen(str) + 1;
     319              :     // Next expected offset is equal to the current total size of the
     320              :     // concatenated string
     321            8 :     msg->offset = _bf_printer_total_size(printer);
     322            8 :     msg->str = strdup(str);
     323            8 :     if (!msg->str)
     324              :         return NULL;
     325              : 
     326            8 :     r = bf_list_add_tail(&printer->msgs, msg);
     327            8 :     if (r) {
     328            0 :         bf_err("failed to add a new printer message to the printer context");
     329            0 :         return NULL;
     330              :     }
     331              : 
     332            8 :     return TAKE_PTR(msg);
     333              : }
     334              : 
     335            0 : 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            0 :     bf_assert(printer);
     342            0 :     bf_assert(str);
     343            0 :     bf_assert(str_len);
     344              : 
     345            0 :     _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            0 :     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            0 :         _str_len = _bf_printer_total_size(printer);
     358            0 :         _str = malloc(_str_len);
     359            0 :         if (!_str)
     360              :             return -ENOMEM;
     361              : 
     362            0 :         bf_list_foreach (&printer->msgs, msg_node) {
     363            0 :             struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
     364            0 :             memcpy(_str + msg->offset, msg->str, msg->len);
     365              :         }
     366              :     }
     367              : 
     368            0 :     *str = TAKE_PTR(_str);
     369            0 :     *str_len = _str_len;
     370              : 
     371            0 :     return 0;
     372              : }
        

Generated by: LCOV version 2.0-1