LCOV - code coverage report
Current view: top level - bpfilter/cgen - printer.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 65.8 % 149 98
Test Date: 2025-05-27 09:39:07 Functions: 80.0 % 15 12

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

Generated by: LCOV version 2.0-1