LCOV - code coverage report
Current view: top level - lib/telemetry - telemetry_json.h (source / functions) Hit Total Coverage
Test: Code coverage Lines: 94 102 92.2 %
Date: 2025-02-01 18:54:23 Functions: 11 11 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 58 78 74.4 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2020 Intel Corporation
       3                 :            :  */
       4                 :            : 
       5                 :            : #ifndef _RTE_TELEMETRY_JSON_H_
       6                 :            : #define _RTE_TELEMETRY_JSON_H_
       7                 :            : 
       8                 :            : #include <inttypes.h>
       9                 :            : #include <stdarg.h>
      10                 :            : #include <stdio.h>
      11                 :            : #include <stdlib.h>
      12                 :            : #include <rte_common.h>
      13                 :            : #include <rte_telemetry.h>
      14                 :            : 
      15                 :            : /**
      16                 :            :  * @file
      17                 :            :  * Internal Telemetry Utility functions
      18                 :            :  *
      19                 :            :  * This file contains small inline functions to make it easier for applications
      20                 :            :  * to build up valid JSON responses to telemetry requests.
      21                 :            :  */
      22                 :            : 
      23                 :            : /**
      24                 :            :  * @internal
      25                 :            :  * Copies a value into a buffer if the buffer has enough available space.
      26                 :            :  * Nothing written to buffer if an overflow occurs.
      27                 :            :  * This function is not for use for values larger than given buffer length.
      28                 :            :  */
      29                 :            : __rte_format_printf(3, 4)
      30                 :            : static inline int
      31                 :        152 : __json_snprintf(char *buf, const int len, const char *format, ...)
      32                 :            : {
      33                 :            :         va_list ap;
      34                 :            :         char tmp[4];
      35                 :            :         char *newbuf;
      36                 :            :         int ret;
      37                 :            : 
      38         [ +  - ]:        152 :         if (len == 0)
      39                 :            :                 return 0;
      40                 :            : 
      41                 :            :         /* to ensure unmodified if we overflow, we save off any values currently in buf
      42                 :            :          * before we printf, if they are short enough. We restore them on error.
      43                 :            :          */
      44         [ +  + ]:        152 :         if (strnlen(buf, sizeof(tmp)) < sizeof(tmp)) {
      45                 :            :                 strcpy(tmp, buf);  /* strcpy is safe as we know the length */
      46                 :        150 :                 va_start(ap, format);
      47         [ +  + ]:        150 :                 ret = vsnprintf(buf, len, format, ap);
      48                 :        150 :                 va_end(ap);
      49         [ +  + ]:        150 :                 if (ret > 0 && ret < len)
      50                 :            :                         return ret;
      51                 :            :                 strcpy(buf, tmp);  /* restore on error */
      52                 :          5 :                 return 0;
      53                 :            :         }
      54                 :            : 
      55                 :            :         /* in normal operations should never hit this, but can do if buffer is
      56                 :            :          * incorrectly initialized e.g. in unit test cases
      57                 :            :          */
      58                 :          2 :         newbuf = malloc(len);
      59         [ +  - ]:          2 :         if (newbuf == NULL)
      60                 :            :                 return 0;
      61                 :            : 
      62         [ +  - ]:          2 :         va_start(ap, format);
      63                 :            :         ret = vsnprintf(newbuf, len, format, ap);
      64                 :          2 :         va_end(ap);
      65         [ +  - ]:          2 :         if (ret > 0 && ret < len) {
      66                 :            :                 strcpy(buf, newbuf);
      67                 :          2 :                 free(newbuf);
      68                 :          2 :                 return ret;
      69                 :            :         }
      70                 :          0 :         free(newbuf);
      71                 :          0 :         return 0; /* nothing written or modified */
      72                 :            : }
      73                 :            : 
      74                 :            : static const char control_chars[0x20] = {
      75                 :            :                 ['\n'] = 'n',
      76                 :            :                 ['\r'] = 'r',
      77                 :            :                 ['\t'] = 't',
      78                 :            : };
      79                 :            : 
      80                 :            : /**
      81                 :            :  * @internal
      82                 :            :  * Function that does the actual printing, used by __json_format_str. Modifies buffer
      83                 :            :  * directly, but returns 0 on overflow. Otherwise returns number of chars written to buffer.
      84                 :            :  */
      85                 :            : static inline int
      86                 :         56 : __json_format_str_to_buf(char *buf, const int len,
      87                 :            :                 const char *prefix, const char *str, const char *suffix)
      88                 :            : {
      89                 :            :         int bufidx = 0;
      90                 :            : 
      91   [ +  +  +  - ]:        300 :         while (*prefix != '\0' && bufidx < len)
      92                 :        244 :                 buf[bufidx++] = *prefix++;
      93         [ +  - ]:         56 :         if (bufidx >= len)
      94                 :            :                 return 0;
      95                 :            : 
      96         [ +  + ]:        543 :         while (*str != '\0') {
      97         [ +  + ]:        490 :                 if (*str < (int)RTE_DIM(control_chars)) {
      98                 :         13 :                         int idx = *str;  /* compilers don't like char type as index */
      99         [ +  - ]:         13 :                         if (control_chars[idx] != 0) {
     100                 :         13 :                                 buf[bufidx++] = '\\';
     101                 :         13 :                                 buf[bufidx++] = control_chars[idx];
     102                 :            :                         }
     103         [ +  + ]:        477 :                 } else if (*str == '"' || *str == '\\') {
     104                 :          7 :                         buf[bufidx++] = '\\';
     105                 :          7 :                         buf[bufidx++] = *str;
     106                 :            :                 } else
     107                 :        470 :                         buf[bufidx++] = *str;
     108                 :            :                 /* we always need space for (at minimum) closing quote and null character.
     109                 :            :                  * Ensuring at least two free characters also means we can always take an
     110                 :            :                  * escaped character like "\n" without overflowing
     111                 :            :                  */
     112         [ +  + ]:        490 :                 if (bufidx > len - 2)
     113                 :            :                         return 0;
     114                 :        487 :                 str++;
     115                 :            :         }
     116                 :            : 
     117   [ +  +  +  - ]:        156 :         while (*suffix != '\0' && bufidx < len)
     118                 :        103 :                 buf[bufidx++] = *suffix++;
     119         [ +  - ]:         53 :         if (bufidx >= len)
     120                 :            :                 return 0;
     121                 :            : 
     122                 :         53 :         buf[bufidx] = '\0';
     123                 :         53 :         return bufidx;
     124                 :            : }
     125                 :            : 
     126                 :            : /**
     127                 :            :  * @internal
     128                 :            :  * This function acts the same as __json_snprintf(buf, len, "%s%s%s", prefix, str, suffix)
     129                 :            :  * except that it does proper escaping of "str" as necessary. Prefix and suffix should be compile-
     130                 :            :  * time constants, or values not needing escaping.
     131                 :            :  * Drops any invalid characters we don't support
     132                 :            :  */
     133                 :            : static inline int
     134                 :         56 : __json_format_str(char *buf, const int len, const char *prefix, const char *str, const char *suffix)
     135                 :            : {
     136                 :            :         int ret;
     137                 :         56 :         char saved[4] = "";
     138                 :            :         char *tmp;
     139                 :            : 
     140         [ +  - ]:         56 :         if (strnlen(buf, sizeof(saved)) < sizeof(saved)) {
     141                 :            :                 /* we have only a few bytes in buffer, so save them off to restore on error*/
     142                 :            :                 strcpy(saved, buf);
     143                 :         56 :                 ret = __json_format_str_to_buf(buf, len, prefix, str, suffix);
     144         [ +  + ]:         56 :                 if (ret == 0)
     145                 :            :                         strcpy(buf, saved); /* restore */
     146                 :         56 :                 return ret;
     147                 :            :         }
     148                 :            : 
     149                 :          0 :         tmp = malloc(len);
     150         [ #  # ]:          0 :         if (tmp == NULL)
     151                 :            :                 return 0;
     152                 :            : 
     153                 :          0 :         ret = __json_format_str_to_buf(tmp, len, prefix, str, suffix);
     154         [ #  # ]:          0 :         if (ret > 0)
     155                 :            :                 strcpy(buf, tmp);
     156                 :            : 
     157                 :          0 :         free(tmp);
     158                 :          0 :         return ret;
     159                 :            : }
     160                 :            : 
     161                 :            : /* Copies an empty array into the provided buffer. */
     162                 :            : static inline int
     163                 :            : rte_tel_json_empty_array(char *buf, const int len, const int used)
     164                 :            : {
     165                 :         35 :         return used + __json_snprintf(buf + used, len - used, "[]");
     166                 :            : }
     167                 :            : 
     168                 :            : /* Copies an empty object into the provided buffer. */
     169                 :            : static inline int
     170                 :            : rte_tel_json_empty_obj(char *buf, const int len, const int used)
     171                 :            : {
     172                 :         14 :         return used + __json_snprintf(buf + used, len - used, "{}");
     173                 :            : }
     174                 :            : 
     175                 :            : /* Copies a string into the provided buffer, in JSON format. */
     176                 :            : static inline int
     177                 :            : rte_tel_json_str(char *buf, const int len, const int used, const char *str)
     178                 :            : {
     179                 :          3 :         return used + __json_format_str(buf + used, len - used, "\"", str, "\"");
     180                 :            : }
     181                 :            : 
     182                 :            : /* Appends a string into the JSON array in the provided buffer. */
     183                 :            : static inline int
     184                 :         37 : rte_tel_json_add_array_string(char *buf, const int len, const int used,
     185                 :            :                 const char *str)
     186                 :            : {
     187                 :         37 :         int ret, end = used - 1; /* strip off final delimiter */
     188         [ +  + ]:         37 :         if (used <= 2) /* assume empty, since minimum is '[]' */
     189                 :         22 :                 return __json_format_str(buf, len, "[\"", str, "\"]");
     190                 :            : 
     191                 :         15 :         ret = __json_format_str(buf + end, len - end, ",\"", str, "\"]");
     192         [ +  + ]:         15 :         return ret == 0 ? used : end + ret;
     193                 :            : }
     194                 :            : 
     195                 :            : /* Appends an integer into the JSON array in the provided buffer. */
     196                 :            : static inline int
     197                 :         26 : rte_tel_json_add_array_int(char *buf, const int len, const int used, int64_t val)
     198                 :            : {
     199                 :         26 :         int ret, end = used - 1; /* strip off final delimiter */
     200         [ +  + ]:         26 :         if (used <= 2) /* assume empty, since minimum is '[]' */
     201                 :          5 :                 return __json_snprintf(buf, len, "[%"PRId64"]", val);
     202                 :            : 
     203                 :         21 :         ret = __json_snprintf(buf + end, len - end, ",%"PRId64"]", val);
     204         [ +  - ]:         21 :         return ret == 0 ? used : end + ret;
     205                 :            : }
     206                 :            : 
     207                 :            : /* Appends a uint64_t into the JSON array in the provided buffer. */
     208                 :            : static inline int
     209                 :         35 : rte_tel_json_add_array_uint(char *buf, const int len, const int used,
     210                 :            :                 uint64_t val)
     211                 :            : {
     212                 :         35 :         int ret, end = used - 1; /* strip off final delimiter */
     213         [ +  + ]:         35 :         if (used <= 2) /* assume empty, since minimum is '[]' */
     214                 :          5 :                 return __json_snprintf(buf, len, "[%"PRIu64"]", val);
     215                 :            : 
     216                 :         30 :         ret = __json_snprintf(buf + end, len - end, ",%"PRIu64"]", val);
     217         [ +  - ]:         30 :         return ret == 0 ? used : end + ret;
     218                 :            : }
     219                 :            : 
     220                 :            : /*
     221                 :            :  * Add a new element with raw JSON value to the JSON array stored in the
     222                 :            :  * provided buffer.
     223                 :            :  */
     224                 :            : static inline int
     225                 :         10 : rte_tel_json_add_array_json(char *buf, const int len, const int used,
     226                 :            :                 const char *str)
     227                 :            : {
     228                 :         10 :         int ret, end = used - 1; /* strip off final delimiter */
     229         [ +  + ]:         10 :         if (used <= 2) /* assume empty, since minimum is '[]' */
     230                 :          5 :                 return __json_snprintf(buf, len, "[%s]", str);
     231                 :            : 
     232                 :          5 :         ret = __json_snprintf(buf + end, len - end, ",%s]", str);
     233         [ +  - ]:          5 :         return ret == 0 ? used : end + ret;
     234                 :            : }
     235                 :            : 
     236                 :            : /**
     237                 :            :  * Add a new element with uint64_t value to the JSON object stored in the
     238                 :            :  * provided buffer.
     239                 :            :  */
     240                 :            : static inline int
     241                 :         14 : rte_tel_json_add_obj_uint(char *buf, const int len, const int used,
     242                 :            :                 const char *name, uint64_t val)
     243                 :            : {
     244                 :         14 :         int ret, end = used - 1;
     245         [ +  + ]:         14 :         if (used <= 2) /* assume empty, since minimum is '{}' */
     246                 :          4 :                 return __json_snprintf(buf, len, "{\"%s\":%"PRIu64"}", name,
     247                 :            :                                 val);
     248                 :            : 
     249                 :         10 :         ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRIu64"}",
     250                 :            :                         name, val);
     251         [ +  + ]:         10 :         return ret == 0 ? used : end + ret;
     252                 :            : }
     253                 :            : 
     254                 :            : /**
     255                 :            :  * Add a new element with int value to the JSON object stored in the
     256                 :            :  * provided buffer.
     257                 :            :  */
     258                 :            : static inline int
     259                 :          5 : rte_tel_json_add_obj_int(char *buf, const int len, const int used,
     260                 :            :                 const char *name, int64_t val)
     261                 :            : {
     262                 :          5 :         int ret, end = used - 1;
     263         [ +  + ]:          5 :         if (used <= 2) /* assume empty, since minimum is '{}' */
     264                 :          1 :                 return __json_snprintf(buf, len, "{\"%s\":%"PRId64"}", name,
     265                 :            :                                 val);
     266                 :            : 
     267                 :          4 :         ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRId64"}",
     268                 :            :                         name, val);
     269         [ +  - ]:          4 :         return ret == 0 ? used : end + ret;
     270                 :            : }
     271                 :            : 
     272                 :            : /**
     273                 :            :  * Add a new element with string value to the JSON object stored in the
     274                 :            :  * provided buffer.
     275                 :            :  */
     276                 :            : static inline int
     277                 :         16 : rte_tel_json_add_obj_str(char *buf, const int len, const int used,
     278                 :            :                 const char *name, const char *val)
     279                 :            : {
     280                 :            :         char tmp_name[RTE_TEL_MAX_STRING_LEN + 5];
     281         [ +  + ]:         16 :         int ret, end = used - 1;
     282                 :            : 
     283                 :            :         /* names are limited to certain characters so need no escaping */
     284                 :            :         snprintf(tmp_name, sizeof(tmp_name), "{\"%s\":\"", name);
     285         [ +  + ]:         16 :         if (used <= 2) /* assume empty, since minimum is '{}' */
     286                 :          5 :                 return __json_format_str(buf, len, tmp_name, val, "\"}");
     287                 :            : 
     288                 :         11 :         tmp_name[0] = ',';  /* replace '{' with ',' at start */
     289                 :         11 :         ret = __json_format_str(buf + end, len - end, tmp_name, val, "\"}");
     290         [ +  - ]:         11 :         return ret == 0 ? used : end + ret;
     291                 :            : }
     292                 :            : 
     293                 :            : /**
     294                 :            :  * Add a new element with raw JSON value to the JSON object stored in the
     295                 :            :  * provided buffer.
     296                 :            :  */
     297                 :            : static inline int
     298                 :         13 : rte_tel_json_add_obj_json(char *buf, const int len, const int used,
     299                 :            :                 const char *name, const char *val)
     300                 :            : {
     301                 :         13 :         int ret, end = used - 1;
     302         [ +  + ]:         13 :         if (used <= 2) /* assume empty, since minimum is '{}' */
     303                 :          7 :                 return __json_snprintf(buf, len, "{\"%s\":%s}", name, val);
     304                 :            : 
     305                 :          6 :         ret = __json_snprintf(buf + end, len - end, ",\"%s\":%s}",
     306                 :            :                         name, val);
     307         [ +  - ]:          6 :         return ret == 0 ? used : end + ret;
     308                 :            : }
     309                 :            : 
     310                 :            : #endif /*_RTE_TELEMETRY_JSON_H_*/

Generated by: LCOV version 1.14