LCOV - code coverage report
Current view: top level - lib/dpkg - ehandle.c (source / functions) Hit Total Coverage
Test: dpkg 1.21.11 C code coverage Lines: 158 203 77.8 %
Date: 2022-12-03 00:40:01 Functions: 22 26 84.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 38 60 63.3 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * libdpkg - Debian packaging suite library routines
       3                 :            :  * ehandle.c - error handling
       4                 :            :  *
       5                 :            :  * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
       6                 :            :  * Copyright © 2008-2014 Guillem Jover <guillem@debian.org>
       7                 :            :  *
       8                 :            :  * This is free software; you can redistribute it and/or modify
       9                 :            :  * it under the terms of the GNU General Public License as published by
      10                 :            :  * the Free Software Foundation; either version 2 of the License, or
      11                 :            :  * (at your option) any later version.
      12                 :            :  *
      13                 :            :  * This is distributed in the hope that it will be useful,
      14                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16                 :            :  * GNU General Public License for more details.
      17                 :            :  *
      18                 :            :  * You should have received a copy of the GNU General Public License
      19                 :            :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      20                 :            :  */
      21                 :            : 
      22                 :            : #include <config.h>
      23                 :            : #include <compat.h>
      24                 :            : 
      25                 :            : #include <errno.h>
      26                 :            : #include <string.h>
      27                 :            : #include <unistd.h>
      28                 :            : #include <stdarg.h>
      29                 :            : #include <stdlib.h>
      30                 :            : #include <stdio.h>
      31                 :            : 
      32                 :            : #include <dpkg/macros.h>
      33                 :            : #include <dpkg/i18n.h>
      34                 :            : #include <dpkg/progname.h>
      35                 :            : #include <dpkg/color.h>
      36                 :            : #include <dpkg/ehandle.h>
      37                 :            : 
      38                 :            : /* Incremented when we do some kind of generally necessary operation,
      39                 :            :  * so that loops &c know to quit if we take an error exit. Decremented
      40                 :            :  * again afterwards. */
      41                 :            : volatile int onerr_abort = 0;
      42                 :            : 
      43                 :            : #define NCALLS 2
      44                 :            : 
      45                 :            : struct cleanup_entry {
      46                 :            :   struct cleanup_entry *next;
      47                 :            :   struct {
      48                 :            :     int mask;
      49                 :            :     void (*call)(int argc, void **argv);
      50                 :            :   } calls[NCALLS];
      51                 :            :   int cpmask, cpvalue;
      52                 :            :   int argc;
      53                 :            :   void *argv[1];
      54                 :            : };
      55                 :            : 
      56                 :            : struct error_context {
      57                 :            :   struct error_context *next;
      58                 :            : 
      59                 :            :   enum {
      60                 :            :     HANDLER_TYPE_FUNC,
      61                 :            :     HANDLER_TYPE_JUMP,
      62                 :            :   } handler_type;
      63                 :            : 
      64                 :            :   union {
      65                 :            :     error_handler_func *func;
      66                 :            :     jmp_buf *jump;
      67                 :            :   } handler;
      68                 :            : 
      69                 :            :   struct {
      70                 :            :     error_printer_func *func;
      71                 :            :     const void *data;
      72                 :            :   } printer;
      73                 :            : 
      74                 :            :   struct cleanup_entry *cleanups;
      75                 :            : 
      76                 :            :   char *errmsg;
      77                 :            : };
      78                 :            : 
      79                 :            : static struct error_context *volatile econtext = NULL;
      80                 :            : 
      81                 :            : /**
      82                 :            :  * Emergency variables.
      83                 :            :  *
      84                 :            :  * These are used when the system is out of resources, and we want to be able
      85                 :            :  * to proceed anyway at least to the point of a controlled shutdown.
      86                 :            :  */
      87                 :            : static struct {
      88                 :            :   struct cleanup_entry ce;
      89                 :            :   void *args[20];
      90                 :            : 
      91                 :            :   /**
      92                 :            :    * Emergency error message buffer.
      93                 :            :    *
      94                 :            :    * The size is estimated using the following heuristic:
      95                 :            :    * - 6x255 For inserted strings (%.255s &c in fmt; and %s with limited
      96                 :            :    *         length arg).
      97                 :            :    * - 1x255 For constant text.
      98                 :            :    * - 1x255 For strerror().
      99                 :            :    * - And the total doubled just in case.
     100                 :            :    */
     101                 :            :   char errmsg[4096];
     102                 :            : } emergency;
     103                 :            : 
     104                 :            : /**
     105                 :            :  * Default fatal error handler.
     106                 :            :  *
     107                 :            :  * This handler performs all error unwinding for the current context, and
     108                 :            :  * terminates the program with an error exit code.
     109                 :            :  */
     110                 :            : void
     111                 :         68 : catch_fatal_error(void)
     112                 :            : {
     113                 :         68 :   pop_error_context(ehflag_bombout);
     114                 :         68 :   exit(2);
     115                 :            : }
     116                 :            : 
     117                 :            : void
     118                 :         68 : print_fatal_error(const char *emsg, const void *data)
     119                 :            : {
     120                 :         68 :   fprintf(stderr, "%s%s:%s %s%s:%s %s\n",
     121                 :            :           color_get(COLOR_PROG), dpkg_get_progname(), color_reset(),
     122                 :            :           color_get(COLOR_ERROR), _("error"), color_reset(), emsg);
     123                 :         68 : }
     124                 :            : 
     125                 :            : static void
     126                 :          4 : print_abort_error(const char *etype, const char *emsg)
     127                 :            : {
     128                 :          4 :   fprintf(stderr, _("%s%s%s: %s%s:%s\n %s\n"),
     129                 :            :           color_get(COLOR_PROG), dpkg_get_progname(), color_reset(),
     130                 :            :           color_get(COLOR_ERROR), etype, color_reset(), emsg);
     131                 :          4 : }
     132                 :            : 
     133                 :            : static struct error_context *
     134                 :        563 : error_context_new(void)
     135                 :            : {
     136                 :            :   struct error_context *necp;
     137                 :            : 
     138                 :        563 :   necp = malloc(sizeof(*necp));
     139         [ -  + ]:        563 :   if (!necp)
     140                 :          0 :     ohshite(_("out of memory for new error context"));
     141                 :        563 :   necp->next= econtext;
     142                 :        563 :   necp->cleanups= NULL;
     143                 :        563 :   necp->errmsg = NULL;
     144                 :        563 :   econtext= necp;
     145                 :            : 
     146                 :        563 :   return necp;
     147                 :            : }
     148                 :            : 
     149                 :            : static void
     150                 :        941 : set_error_printer(struct error_context *ec, error_printer_func *func,
     151                 :            :                   const void *data)
     152                 :            : {
     153                 :        941 :   ec->printer.func = func;
     154                 :        941 :   ec->printer.data = data;
     155                 :        941 : }
     156                 :            : 
     157                 :            : static void
     158                 :        559 : set_func_handler(struct error_context *ec, error_handler_func *func)
     159                 :            : {
     160                 :        559 :   ec->handler_type = HANDLER_TYPE_FUNC;
     161                 :        559 :   ec->handler.func = func;
     162                 :        559 : }
     163                 :            : 
     164                 :            : static void
     165                 :         33 : set_jump_handler(struct error_context *ec, jmp_buf *jump)
     166                 :            : {
     167                 :         33 :   ec->handler_type = HANDLER_TYPE_JUMP;
     168                 :         33 :   ec->handler.jump = jump;
     169                 :         33 : }
     170                 :            : 
     171                 :            : static void
     172                 :        506 : error_context_errmsg_free(struct error_context *ec)
     173                 :            : {
     174         [ +  - ]:        506 :   if (ec->errmsg != emergency.errmsg)
     175                 :        506 :     free(ec->errmsg);
     176                 :        506 :   ec->errmsg = NULL;
     177                 :        506 : }
     178                 :            : 
     179                 :            : static void
     180                 :         84 : error_context_errmsg_set(struct error_context *ec, char *errmsg)
     181                 :            : {
     182                 :         84 :   error_context_errmsg_free(ec);
     183                 :         84 :   ec->errmsg = errmsg;
     184                 :         84 : }
     185                 :            : 
     186                 :            : static int DPKG_ATTR_VPRINTF(1)
     187                 :         76 : error_context_errmsg_format(const char *fmt, va_list args)
     188                 :            : {
     189                 :            :   va_list args_copy;
     190                 :         76 :   char *errmsg = NULL;
     191                 :            :   int rc;
     192                 :            : 
     193                 :         76 :   va_copy(args_copy, args);
     194                 :         76 :   rc = vasprintf(&errmsg, fmt, args_copy);
     195                 :         76 :   va_end(args_copy);
     196                 :            : 
     197                 :            :   /* If the message was constructed successfully, at least we have some
     198                 :            :    * error message, which is better than nothing. */
     199         [ +  - ]:         76 :   if (rc >= 0)
     200                 :         76 :     error_context_errmsg_set(econtext, errmsg);
     201                 :            : 
     202         [ -  + ]:         76 :   if (rc < 0) {
     203                 :            :     /* If there was any error, just use the emergency error message buffer,
     204                 :            :      * even if it ends up being truncated, at least we will have a big part
     205                 :            :      * of the problem. */
     206                 :          0 :     rc = vsnprintf(emergency.errmsg, sizeof(emergency.errmsg), fmt, args);
     207                 :            : 
     208                 :            :     /* Return failure only if we get truncated. */
     209         [ #  # ]:          0 :     if (rc >= (int)sizeof(emergency.errmsg))
     210                 :          0 :       rc = -1;
     211                 :            : 
     212                 :          0 :     error_context_errmsg_set(econtext, emergency.errmsg);
     213                 :            :   }
     214                 :            : 
     215                 :         76 :   return rc;
     216                 :            : }
     217                 :            : 
     218                 :            : void
     219                 :        559 : push_error_context_func(error_handler_func *handler,
     220                 :            :                         error_printer_func *printer,
     221                 :            :                         const void *printer_data)
     222                 :            : {
     223                 :            :   struct error_context *ec;
     224                 :            : 
     225                 :        559 :   ec = error_context_new();
     226                 :        559 :   set_error_printer(ec, printer, printer_data);
     227                 :        559 :   set_func_handler(ec, handler);
     228                 :        559 :   onerr_abort = 0;
     229                 :        559 : }
     230                 :            : 
     231                 :            : void
     232                 :          4 : push_error_context_jump(jmp_buf *jumper,
     233                 :            :                         error_printer_func *printer,
     234                 :            :                         const void *printer_data)
     235                 :            : {
     236                 :            :   struct error_context *ec;
     237                 :            : 
     238                 :          4 :   ec = error_context_new();
     239                 :          4 :   set_error_printer(ec, printer, printer_data);
     240                 :          4 :   set_jump_handler(ec, jumper);
     241                 :          4 :   onerr_abort = 0;
     242                 :          4 : }
     243                 :            : 
     244                 :            : void
     245                 :        417 : push_error_context(void)
     246                 :            : {
     247                 :        417 :   push_error_context_func(catch_fatal_error, print_fatal_error, NULL);
     248                 :        417 : }
     249                 :            : 
     250                 :            : static void
     251                 :          1 : print_cleanup_error(const char *emsg, const void *data)
     252                 :            : {
     253                 :          1 :   print_abort_error(_("error while cleaning up"), emsg);
     254                 :          1 : }
     255                 :            : 
     256                 :            : static void
     257                 :        419 : run_cleanups(struct error_context *econ, int flagsetin)
     258                 :            : {
     259                 :            :   static volatile int preventrecurse= 0;
     260                 :            :   struct cleanup_entry *volatile cep;
     261                 :            :   struct cleanup_entry *ncep;
     262                 :            :   struct error_context recurserr, *oldecontext;
     263                 :            :   jmp_buf recurse_jump;
     264                 :            :   volatile int i, flagset;
     265                 :            : 
     266         [ +  + ]:        419 :   if (econ->printer.func)
     267                 :         70 :     econ->printer.func(econ->errmsg, econ->printer.data);
     268                 :            : 
     269         [ -  + ]:        419 :   if (++preventrecurse > 3) {
     270                 :          0 :     onerr_abort++;
     271                 :          0 :     print_cleanup_error(_("too many nested errors during error recovery"), NULL);
     272                 :          0 :     flagset= 0;
     273                 :            :   } else {
     274                 :        419 :     flagset= flagsetin;
     275                 :            :   }
     276                 :        419 :   cep= econ->cleanups;
     277                 :        419 :   oldecontext= econtext;
     278         [ +  + ]:        448 :   while (cep) {
     279         [ +  + ]:         87 :     for (i=0; i<NCALLS; i++) {
     280   [ +  +  +  - ]:         58 :       if (cep->calls[i].call && cep->calls[i].mask & flagset) {
     281         [ +  + ]:         29 :         if (setjmp(recurse_jump)) {
     282                 :          1 :           run_cleanups(&recurserr, ehflag_bombout | ehflag_recursiveerror);
     283                 :          1 :           error_context_errmsg_free(&recurserr);
     284                 :            :         } else {
     285                 :         29 :           memset(&recurserr, 0, sizeof(recurserr));
     286                 :         29 :           set_error_printer(&recurserr, print_cleanup_error, NULL);
     287                 :         29 :           set_jump_handler(&recurserr, &recurse_jump);
     288                 :         29 :           econtext= &recurserr;
     289                 :         29 :           cep->calls[i].call(cep->argc,cep->argv);
     290                 :            :         }
     291                 :         29 :         econtext= oldecontext;
     292                 :            :       }
     293                 :            :     }
     294                 :         29 :     flagset &= cep->cpmask;
     295                 :         29 :     flagset |= cep->cpvalue;
     296                 :         29 :     ncep= cep->next;
     297         [ +  - ]:         29 :     if (cep != &emergency.ce) free(cep);
     298                 :         29 :     cep= ncep;
     299                 :            :   }
     300                 :        419 :   preventrecurse--;
     301                 :        419 : }
     302                 :            : 
     303                 :            : /**
     304                 :            :  * Push an error cleanup checkpoint.
     305                 :            :  *
     306                 :            :  * This will arrange that when pop_error_context() is called, all previous
     307                 :            :  * cleanups will be executed with
     308                 :            :  *   flagset = (original_flagset & mask) | value
     309                 :            :  * where original_flagset is the argument to pop_error_context() (as
     310                 :            :  * modified by any checkpoint which was pushed later).
     311                 :            :  */
     312                 :          0 : void push_checkpoint(int mask, int value) {
     313                 :            :   struct cleanup_entry *cep;
     314                 :            :   int i;
     315                 :            : 
     316                 :          0 :   cep = malloc(sizeof(*cep) + sizeof(void *));
     317         [ #  # ]:          0 :   if (cep == NULL) {
     318                 :          0 :     onerr_abort++;
     319                 :          0 :     ohshite(_("out of memory for new cleanup entry"));
     320                 :            :   }
     321                 :            : 
     322         [ #  # ]:          0 :   for (i=0; i<NCALLS; i++) { cep->calls[i].call=NULL; cep->calls[i].mask=0; }
     323                 :          0 :   cep->cpmask= mask; cep->cpvalue= value;
     324                 :          0 :   cep->argc= 0; cep->argv[0]= NULL;
     325                 :          0 :   cep->next= econtext->cleanups;
     326                 :          0 :   econtext->cleanups= cep;
     327                 :          0 : }
     328                 :            : 
     329                 :            : static void
     330                 :        153 : cleanup_entry_new(void (*call1)(int argc, void **argv), int mask1,
     331                 :            :                   void (*call2)(int argc, void **argv), int mask2,
     332                 :            :                   unsigned int nargs, va_list vargs)
     333                 :            : {
     334                 :            :   struct cleanup_entry *cep;
     335                 :            :   void **argv;
     336                 :        153 :   int e = 0;
     337                 :            :   va_list args;
     338                 :            : 
     339                 :        153 :   onerr_abort++;
     340                 :            : 
     341                 :        153 :   cep = malloc(sizeof(*cep) + sizeof(void *) * (nargs + 1));
     342         [ -  + ]:        153 :   if (!cep) {
     343         [ #  # ]:          0 :     if (nargs > array_count(emergency.args))
     344                 :          0 :       ohshite(_("out of memory for new cleanup entry with many arguments"));
     345                 :          0 :     e= errno; cep= &emergency.ce;
     346                 :            :   }
     347                 :        153 :   cep->calls[0].call= call1; cep->calls[0].mask= mask1;
     348                 :        153 :   cep->calls[1].call= call2; cep->calls[1].mask= mask2;
     349                 :        153 :   cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs;
     350                 :            : 
     351                 :        153 :   va_copy(args, vargs);
     352                 :        153 :   argv = cep->argv;
     353         [ +  + ]:        319 :   while (nargs-- > 0)
     354                 :        166 :     *argv++ = va_arg(args, void *);
     355                 :        153 :   *argv++ = NULL;
     356                 :        153 :   va_end(args);
     357                 :        153 :   cep->next= econtext->cleanups;
     358                 :        153 :   econtext->cleanups= cep;
     359         [ -  + ]:        153 :   if (cep == &emergency.ce) {
     360                 :          0 :     errno = e;
     361                 :          0 :     ohshite(_("out of memory for new cleanup entry"));
     362                 :            :   }
     363                 :            : 
     364                 :        153 :   onerr_abort--;
     365                 :        153 : }
     366                 :            : 
     367                 :            : void
     368                 :        153 : push_cleanup(void (*call)(int argc, void **argv), int mask,
     369                 :            :              unsigned int nargs, ...)
     370                 :            : {
     371                 :            :   va_list args;
     372                 :            : 
     373                 :        153 :   va_start(args, nargs);
     374                 :        153 :   cleanup_entry_new(call, mask, NULL, 0, nargs, args);
     375                 :        153 :   va_end(args);
     376                 :        153 : }
     377                 :            : 
     378                 :            : void
     379                 :          0 : push_cleanup_fallback(void (*call1)(int argc, void **argv), int mask1,
     380                 :            :                       void (*call2)(int argc, void **argv), int mask2,
     381                 :            :                       unsigned int nargs, ...)
     382                 :            : {
     383                 :            :   va_list args;
     384                 :            : 
     385                 :          0 :   va_start(args, nargs);
     386                 :          0 :   cleanup_entry_new(call1, mask1, call2, mask2, nargs, args);
     387                 :          0 :   va_end(args);
     388                 :          0 : }
     389                 :            : 
     390                 :        124 : void pop_cleanup(int flagset) {
     391                 :            :   struct cleanup_entry *cep;
     392                 :            :   int i;
     393                 :            : 
     394                 :        124 :   cep= econtext->cleanups;
     395                 :        124 :   econtext->cleanups= cep->next;
     396         [ +  + ]:        372 :   for (i=0; i<NCALLS; i++) {
     397   [ +  +  +  + ]:        248 :     if (cep->calls[i].call && cep->calls[i].mask & flagset)
     398                 :          1 :       cep->calls[i].call(cep->argc,cep->argv);
     399                 :            :   }
     400         [ +  - ]:        124 :   if (cep != &emergency.ce) free(cep);
     401                 :        124 : }
     402                 :            : 
     403                 :            : /**
     404                 :            :  * Unwind the current error context by running its registered cleanups.
     405                 :            :  */
     406                 :            : void
     407                 :        418 : pop_error_context(int flagset)
     408                 :            : {
     409                 :            :   struct error_context *tecp;
     410                 :            : 
     411                 :        418 :   tecp = econtext;
     412                 :        418 :   econtext = tecp->next;
     413                 :            : 
     414                 :            :   /* If we are cleaning up normally, do not print anything. */
     415         [ +  + ]:        418 :   if (flagset & ehflag_normaltidy)
     416                 :        349 :     set_error_printer(tecp, NULL, NULL);
     417                 :        418 :   run_cleanups(tecp, flagset);
     418                 :            : 
     419                 :        418 :   error_context_errmsg_free(tecp);
     420                 :        418 :   free(tecp);
     421                 :        418 : }
     422                 :            : 
     423                 :            : static void DPKG_ATTR_NORET
     424                 :         76 : run_error_handler(void)
     425                 :            : {
     426         [ +  + ]:         76 :   if (onerr_abort) {
     427                 :            :     /* We arrived here due to a fatal error from which we cannot recover,
     428                 :            :      * and trying to do so would most probably get us here again. That's
     429                 :            :      * why we will not try to do any error unwinding either. We'll just
     430                 :            :      * abort. Hopefully the user can fix the situation (out of disk, out
     431                 :            :      * of memory, etc). */
     432                 :          3 :     print_abort_error(_("unrecoverable fatal error, aborting"), econtext->errmsg);
     433                 :          3 :     error_context_errmsg_free(econtext);
     434                 :          3 :     exit(2);
     435                 :            :   }
     436                 :            : 
     437         [ -  + ]:         73 :   if (econtext == NULL) {
     438                 :          0 :     print_abort_error(_("outside error context, aborting"),
     439                 :          0 :                       _("an error occurred with no error handling in place"));
     440                 :          0 :     exit(2);
     441         [ +  + ]:         73 :   } else if (econtext->handler_type == HANDLER_TYPE_FUNC) {
     442                 :         69 :     econtext->handler.func();
     443                 :          0 :     internerr("error handler returned unexpectedly!");
     444         [ +  - ]:          4 :   } else if (econtext->handler_type == HANDLER_TYPE_JUMP) {
     445                 :          4 :     longjmp(*econtext->handler.jump, 1);
     446                 :            :   } else {
     447                 :          0 :     internerr("unknown error handler type %d!", econtext->handler_type);
     448                 :            :   }
     449                 :            : }
     450                 :            : 
     451                 :         68 : void ohshit(const char *fmt, ...) {
     452                 :            :   va_list args;
     453                 :            : 
     454                 :         68 :   va_start(args, fmt);
     455                 :         68 :   error_context_errmsg_format(fmt, args);
     456                 :         68 :   va_end(args);
     457                 :            : 
     458                 :         68 :   run_error_handler();
     459                 :            : }
     460                 :            : 
     461                 :            : void
     462                 :          0 : ohshitv(const char *fmt, va_list args)
     463                 :            : {
     464                 :          0 :   error_context_errmsg_format(fmt, args);
     465                 :            : 
     466                 :          0 :   run_error_handler();
     467                 :            : }
     468                 :            : 
     469                 :          8 : void ohshite(const char *fmt, ...) {
     470                 :            :   int e, rc;
     471                 :            :   va_list args;
     472                 :            : 
     473                 :          8 :   e=errno;
     474                 :            : 
     475                 :            :   /* First we construct the formatted message. */
     476                 :          8 :   va_start(args, fmt);
     477                 :          8 :   rc = error_context_errmsg_format(fmt, args);
     478                 :          8 :   va_end(args);
     479                 :            : 
     480                 :            :   /* Then if there was no error we append the string for errno. Otherwise
     481                 :            :    * we just use the emergency error message buffer, and ignore the errno
     482                 :            :    * value, as we will probably have no space left anyway. */
     483         [ +  - ]:          8 :   if (rc > 0) {
     484                 :          8 :     char *errmsg = NULL;
     485                 :            : 
     486                 :          8 :     rc = asprintf(&errmsg, "%s: %s", econtext->errmsg, strerror(e));
     487         [ +  - ]:          8 :     if (rc > 0)
     488                 :          8 :       error_context_errmsg_set(econtext, errmsg);
     489                 :            :   }
     490                 :            : 
     491                 :          8 :   run_error_handler();
     492                 :            : }
     493                 :            : 
     494                 :            : void
     495                 :          0 : do_internerr(const char *file, int line, const char *func, const char *fmt, ...)
     496                 :            : {
     497                 :            :   va_list args;
     498                 :            : 
     499                 :          0 :   va_start(args, fmt);
     500                 :          0 :   error_context_errmsg_format(fmt, args);
     501                 :          0 :   va_end(args);
     502                 :            : 
     503                 :          0 :   fprintf(stderr, "%s%s:%s:%d:%s:%s %s%s:%s %s\n", color_get(COLOR_PROG),
     504                 :            :           dpkg_get_progname(), file, line, func, color_reset(),
     505                 :            :           color_get(COLOR_ERROR), _("internal error"), color_reset(),
     506                 :          0 :           econtext->errmsg);
     507                 :            : 
     508                 :          0 :   error_context_errmsg_free(econtext);
     509                 :            : 
     510                 :          0 :   abort();
     511                 :            : }

Generated by: LCOV version 1.16