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

Generated by: LCOV version 2.0-1