LCOV - code coverage report
Current view: top level - fs/notify - notification.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 87 145 60.0 %
Date: 2014-02-18 Functions: 10 15 66.7 %
Branches: 39 86 45.3 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
       3                 :            :  *
       4                 :            :  *  This program is free software; you can redistribute it and/or modify
       5                 :            :  *  it under the terms of the GNU General Public License as published by
       6                 :            :  *  the Free Software Foundation; either version 2, or (at your option)
       7                 :            :  *  any later version.
       8                 :            :  *
       9                 :            :  *  This program is distributed in the hope that it will be useful,
      10                 :            :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12                 :            :  *  GNU General Public License for more details.
      13                 :            :  *
      14                 :            :  *  You should have received a copy of the GNU General Public License
      15                 :            :  *  along with this program; see the file COPYING.  If not, write to
      16                 :            :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      17                 :            :  */
      18                 :            : 
      19                 :            : /*
      20                 :            :  * Basic idea behind the notification queue: An fsnotify group (like inotify)
      21                 :            :  * sends the userspace notification about events asynchronously some time after
      22                 :            :  * the event happened.  When inotify gets an event it will need to add that
      23                 :            :  * event to the group notify queue.  Since a single event might need to be on
      24                 :            :  * multiple group's notification queues we can't add the event directly to each
      25                 :            :  * queue and instead add a small "event_holder" to each queue.  This event_holder
      26                 :            :  * has a pointer back to the original event.  Since the majority of events are
      27                 :            :  * going to end up on one, and only one, notification queue we embed one
      28                 :            :  * event_holder into each event.  This means we have a single allocation instead
      29                 :            :  * of always needing two.  If the embedded event_holder is already in use by
      30                 :            :  * another group a new event_holder (from fsnotify_event_holder_cachep) will be
      31                 :            :  * allocated and used.
      32                 :            :  */
      33                 :            : 
      34                 :            : #include <linux/fs.h>
      35                 :            : #include <linux/init.h>
      36                 :            : #include <linux/kernel.h>
      37                 :            : #include <linux/list.h>
      38                 :            : #include <linux/module.h>
      39                 :            : #include <linux/mount.h>
      40                 :            : #include <linux/mutex.h>
      41                 :            : #include <linux/namei.h>
      42                 :            : #include <linux/path.h>
      43                 :            : #include <linux/slab.h>
      44                 :            : #include <linux/spinlock.h>
      45                 :            : 
      46                 :            : #include <linux/atomic.h>
      47                 :            : 
      48                 :            : #include <linux/fsnotify_backend.h>
      49                 :            : #include "fsnotify.h"
      50                 :            : 
      51                 :            : static struct kmem_cache *fsnotify_event_cachep;
      52                 :            : static struct kmem_cache *fsnotify_event_holder_cachep;
      53                 :            : /*
      54                 :            :  * This is a magic event we send when the q is too full.  Since it doesn't
      55                 :            :  * hold real event information we just keep one system wide and use it any time
      56                 :            :  * it is needed.  It's refcnt is set 1 at kernel init time and will never
      57                 :            :  * get set to 0 so it will never get 'freed'
      58                 :            :  */
      59                 :            : static struct fsnotify_event *q_overflow_event;
      60                 :            : static atomic_t fsnotify_sync_cookie = ATOMIC_INIT(0);
      61                 :            : 
      62                 :            : /**
      63                 :            :  * fsnotify_get_cookie - return a unique cookie for use in synchronizing events.
      64                 :            :  * Called from fsnotify_move, which is inlined into filesystem modules.
      65                 :            :  */
      66                 :          0 : u32 fsnotify_get_cookie(void)
      67                 :            : {
      68                 :     266388 :         return atomic_inc_return(&fsnotify_sync_cookie);
      69                 :            : }
      70                 :            : EXPORT_SYMBOL_GPL(fsnotify_get_cookie);
      71                 :            : 
      72                 :            : /* return true if the notify queue is empty, false otherwise */
      73                 :          0 : bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group)
      74                 :            : {
      75         [ -  + ]:     347183 :         BUG_ON(!mutex_is_locked(&group->notification_mutex));
      76                 :     694366 :         return list_empty(&group->notification_list) ? true : false;
      77                 :            : }
      78                 :            : 
      79                 :          0 : void fsnotify_get_event(struct fsnotify_event *event)
      80                 :            : {
      81                 :      60323 :         atomic_inc(&event->refcnt);
      82                 :        206 : }
      83                 :            : 
      84                 :          0 : void fsnotify_put_event(struct fsnotify_event *event)
      85                 :            : {
      86            [ + ]:     120636 :         if (!event)
      87                 :          0 :                 return;
      88                 :            : 
      89         [ +  + ]:     120636 :         if (atomic_dec_and_test(&event->refcnt)) {
      90                 :            :                 pr_debug("%s: event=%p\n", __func__, event);
      91                 :            : 
      92         [ +  + ]:      60323 :                 if (event->data_type == FSNOTIFY_EVENT_PATH)
      93                 :      27584 :                         path_put(&event->path);
      94                 :            : 
      95         [ -  + ]:      60323 :                 BUG_ON(!list_empty(&event->private_data_list));
      96                 :            : 
      97                 :      60323 :                 kfree(event->file_name);
      98                 :      60323 :                 put_pid(event->tgid);
      99                 :      60323 :                 kmem_cache_free(fsnotify_event_cachep, event);
     100                 :            :         }
     101                 :            : }
     102                 :            : 
     103                 :          0 : struct fsnotify_event_holder *fsnotify_alloc_event_holder(void)
     104                 :            : {
     105                 :         89 :         return kmem_cache_alloc(fsnotify_event_holder_cachep, GFP_KERNEL);
     106                 :            : }
     107                 :            : 
     108                 :          0 : void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder)
     109                 :            : {
     110 [ #  # ][ #  # ]:        206 :         if (holder)
         [ -  + ][ #  # ]
                 [ #  # ]
     111                 :          0 :                 kmem_cache_free(fsnotify_event_holder_cachep, holder);
     112                 :          0 : }
     113                 :            : 
     114                 :            : /*
     115                 :            :  * Find the private data that the group previously attached to this event when
     116                 :            :  * the group added the event to the notification queue (fsnotify_add_notify_event)
     117                 :            :  */
     118                 :          0 : struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnotify_group *group, struct fsnotify_event *event)
     119                 :            : {
     120                 :            :         struct fsnotify_event_private_data *lpriv;
     121                 :            :         struct fsnotify_event_private_data *priv = NULL;
     122                 :            : 
     123         [ -  + ]:      60117 :         assert_spin_locked(&event->lock);
     124                 :            : 
     125         [ +  - ]:      60117 :         list_for_each_entry(lpriv, &event->private_data_list, event_list) {
     126         [ +  - ]:      60117 :                 if (lpriv->group == group) {
     127                 :            :                         priv = lpriv;
     128                 :            :                         list_del(&priv->event_list);
     129                 :            :                         break;
     130                 :            :                 }
     131                 :            :         }
     132                 :          0 :         return priv;
     133                 :            : }
     134                 :            : 
     135                 :            : /*
     136                 :            :  * Add an event to the group notification queue.  The group can later pull this
     137                 :            :  * event off the queue to deal with.  If the event is successfully added to the
     138                 :            :  * group's notification queue, a reference is taken on event.
     139                 :            :  */
     140                 :          0 : struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event,
     141                 :            :                                                  struct fsnotify_event_private_data *priv,
     142                 :            :                                                  struct fsnotify_event *(*merge)(struct list_head *,
     143                 :            :                                                                                  struct fsnotify_event *))
     144                 :            : {
     145                 :            :         struct fsnotify_event *return_event = NULL;
     146                 :            :         struct fsnotify_event_holder *holder = NULL;
     147                 :      60323 :         struct list_head *list = &group->notification_list;
     148                 :            : 
     149                 :            :         pr_debug("%s: group=%p event=%p priv=%p\n", __func__, group, event, priv);
     150                 :            : 
     151                 :            :         /*
     152                 :            :          * There is one fsnotify_event_holder embedded inside each fsnotify_event.
     153                 :            :          * Check if we expect to be able to use that holder.  If not alloc a new
     154                 :            :          * holder.
     155                 :            :          * For the overflow event it's possible that something will use the in
     156                 :            :          * event holder before we get the lock so we may need to jump back and
     157                 :            :          * alloc a new holder, this can't happen for most events...
     158                 :            :          */
     159         [ +  + ]:      60323 :         if (!list_empty(&event->holder.event_list)) {
     160                 :            : alloc_holder:
     161                 :            :                 holder = fsnotify_alloc_event_holder();
     162         [ #  # ]:          0 :                 if (!holder)
     163                 :            :                         return ERR_PTR(-ENOMEM);
     164                 :            :         }
     165                 :            : 
     166                 :      60234 :         mutex_lock(&group->notification_mutex);
     167                 :            : 
     168         [ -  + ]:      60323 :         if (group->q_len >= group->max_events) {
     169                 :          0 :                 event = q_overflow_event;
     170                 :            : 
     171                 :            :                 /*
     172                 :            :                  * we need to return the overflow event
     173                 :            :                  * which means we need a ref
     174                 :            :                  */
     175                 :            :                 fsnotify_get_event(event);
     176                 :            :                 return_event = event;
     177                 :            : 
     178                 :            :                 /* sorry, no private data on the overflow event */
     179                 :            :                 priv = NULL;
     180                 :            :         }
     181                 :            : 
     182 [ +  + ][ +  + ]:      60323 :         if (!list_empty(list) && merge) {
     183                 :            :                 struct fsnotify_event *tmp;
     184                 :            : 
     185                 :      11297 :                 tmp = merge(list, event);
     186         [ +  + ]:      11297 :                 if (tmp) {
     187                 :        206 :                         mutex_unlock(&group->notification_mutex);
     188                 :            : 
     189         [ -  + ]:        206 :                         if (return_event)
     190                 :          0 :                                 fsnotify_put_event(return_event);
     191         [ +  - ]:        206 :                         if (holder != &event->holder)
     192                 :            :                                 fsnotify_destroy_event_holder(holder);
     193                 :        206 :                         return tmp;
     194                 :            :                 }
     195                 :            :         }
     196                 :            : 
     197                 :            :         spin_lock(&event->lock);
     198                 :            : 
     199         [ +  - ]:      60117 :         if (list_empty(&event->holder.event_list)) {
     200         [ -  + ]:      60117 :                 if (unlikely(holder))
     201                 :            :                         fsnotify_destroy_event_holder(holder);
     202                 :      60117 :                 holder = &event->holder;
     203         [ #  # ]:          0 :         } else if (unlikely(!holder)) {
     204                 :            :                 /* between the time we checked above and got the lock the in
     205                 :            :                  * event holder was used, go back and get a new one */
     206                 :            :                 spin_unlock(&event->lock);
     207                 :          0 :                 mutex_unlock(&group->notification_mutex);
     208                 :            : 
     209         [ #  # ]:          0 :                 if (return_event) {
     210                 :          0 :                         fsnotify_put_event(return_event);
     211                 :            :                         return_event = NULL;
     212                 :            :                 }
     213                 :            : 
     214                 :            :                 goto alloc_holder;
     215                 :            :         }
     216                 :            : 
     217                 :      60117 :         group->q_len++;
     218                 :      60117 :         holder->event = event;
     219                 :            : 
     220                 :            :         fsnotify_get_event(event);
     221                 :      60117 :         list_add_tail(&holder->event_list, list);
     222         [ +  - ]:      60117 :         if (priv)
     223                 :      60117 :                 list_add_tail(&priv->event_list, &event->private_data_list);
     224                 :            :         spin_unlock(&event->lock);
     225                 :      60117 :         mutex_unlock(&group->notification_mutex);
     226                 :            : 
     227                 :      60117 :         wake_up(&group->notification_waitq);
     228                 :      60117 :         kill_fasync(&group->fsn_fa, SIGIO, POLL_IN);
     229                 :      60117 :         return return_event;
     230                 :            : }
     231                 :            : 
     232                 :            : /*
     233                 :            :  * Remove and return the first event from the notification list.  There is a
     234                 :            :  * reference held on this event since it was on the list.  It is the responsibility
     235                 :            :  * of the caller to drop this reference.
     236                 :            :  */
     237                 :          0 : struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group *group)
     238                 :            : {
     239                 :            :         struct fsnotify_event *event;
     240                 :            :         struct fsnotify_event_holder *holder;
     241                 :            : 
     242         [ -  + ]:      60117 :         BUG_ON(!mutex_is_locked(&group->notification_mutex));
     243                 :            : 
     244                 :            :         pr_debug("%s: group=%p\n", __func__, group);
     245                 :            : 
     246                 :      60117 :         holder = list_first_entry(&group->notification_list, struct fsnotify_event_holder, event_list);
     247                 :            : 
     248                 :      60117 :         event = holder->event;
     249                 :            : 
     250                 :            :         spin_lock(&event->lock);
     251                 :      60117 :         holder->event = NULL;
     252                 :      60117 :         list_del_init(&holder->event_list);
     253                 :            :         spin_unlock(&event->lock);
     254                 :            : 
     255                 :            :         /* event == holder means we are referenced through the in event holder */
     256         [ -  + ]:      60117 :         if (holder != &event->holder)
     257                 :            :                 fsnotify_destroy_event_holder(holder);
     258                 :            : 
     259                 :          0 :         group->q_len--;
     260                 :            : 
     261                 :          0 :         return event;
     262                 :            : }
     263                 :            : 
     264                 :            : /*
     265                 :            :  * This will not remove the event, that must be done with fsnotify_remove_notify_event()
     266                 :            :  */
     267                 :          0 : struct fsnotify_event *fsnotify_peek_notify_event(struct fsnotify_group *group)
     268                 :            : {
     269                 :            :         struct fsnotify_event *event;
     270                 :            :         struct fsnotify_event_holder *holder;
     271                 :            : 
     272         [ -  + ]:      60115 :         BUG_ON(!mutex_is_locked(&group->notification_mutex));
     273                 :            : 
     274                 :      60115 :         holder = list_first_entry(&group->notification_list, struct fsnotify_event_holder, event_list);
     275                 :      60115 :         event = holder->event;
     276                 :            : 
     277                 :      60115 :         return event;
     278                 :            : }
     279                 :            : 
     280                 :            : /*
     281                 :            :  * Called when a group is being torn down to clean up any outstanding
     282                 :            :  * event notifications.
     283                 :            :  */
     284                 :          0 : void fsnotify_flush_notify(struct fsnotify_group *group)
     285                 :            : {
     286                 :            :         struct fsnotify_event *event;
     287                 :            :         struct fsnotify_event_private_data *priv;
     288                 :            : 
     289                 :          7 :         mutex_lock(&group->notification_mutex);
     290         [ +  + ]:          9 :         while (!fsnotify_notify_queue_is_empty(group)) {
     291                 :          2 :                 event = fsnotify_remove_notify_event(group);
     292                 :            :                 /* if they don't implement free_event_priv they better not have attached any */
     293         [ +  - ]:          2 :                 if (group->ops->free_event_priv) {
     294                 :            :                         spin_lock(&event->lock);
     295                 :          2 :                         priv = fsnotify_remove_priv_from_event(group, event);
     296                 :            :                         spin_unlock(&event->lock);
     297         [ +  - ]:          2 :                         if (priv)
     298                 :          2 :                                 group->ops->free_event_priv(priv);
     299                 :            :                 }
     300                 :          2 :                 fsnotify_put_event(event); /* matches fsnotify_add_notify_event */
     301                 :            :         }
     302                 :          7 :         mutex_unlock(&group->notification_mutex);
     303                 :          7 : }
     304                 :            : 
     305                 :            : static void initialize_event(struct fsnotify_event *event)
     306                 :            : {
     307                 :      60281 :         INIT_LIST_HEAD(&event->holder.event_list);
     308                 :      60281 :         atomic_set(&event->refcnt, 1);
     309                 :            : 
     310                 :      60281 :         spin_lock_init(&event->lock);
     311                 :            : 
     312                 :      60281 :         INIT_LIST_HEAD(&event->private_data_list);
     313                 :            : }
     314                 :            : 
     315                 :            : /*
     316                 :            :  * Caller damn well better be holding whatever mutex is protecting the
     317                 :            :  * old_holder->event_list and the new_event must be a clean event which
     318                 :            :  * cannot be found anywhere else in the kernel.
     319                 :            :  */
     320                 :          0 : int fsnotify_replace_event(struct fsnotify_event_holder *old_holder,
     321                 :            :                            struct fsnotify_event *new_event)
     322                 :            : {
     323                 :          0 :         struct fsnotify_event *old_event = old_holder->event;
     324                 :            :         struct fsnotify_event_holder *new_holder = &new_event->holder;
     325                 :            : 
     326                 :            :         enum event_spinlock_class {
     327                 :            :                 SPINLOCK_OLD,
     328                 :            :                 SPINLOCK_NEW,
     329                 :            :         };
     330                 :            : 
     331                 :            :         pr_debug("%s: old_event=%p new_event=%p\n", __func__, old_event, new_event);
     332                 :            : 
     333                 :            :         /*
     334                 :            :          * if the new_event's embedded holder is in use someone
     335                 :            :          * screwed up and didn't give us a clean new event.
     336                 :            :          */
     337         [ #  # ]:          0 :         BUG_ON(!list_empty(&new_holder->event_list));
     338                 :            : 
     339                 :          0 :         spin_lock_nested(&old_event->lock, SPINLOCK_OLD);
     340                 :          0 :         spin_lock_nested(&new_event->lock, SPINLOCK_NEW);
     341                 :            : 
     342                 :          0 :         new_holder->event = new_event;
     343                 :          0 :         list_replace_init(&old_holder->event_list, &new_holder->event_list);
     344                 :            : 
     345                 :            :         spin_unlock(&new_event->lock);
     346                 :            :         spin_unlock(&old_event->lock);
     347                 :            : 
     348                 :            :         /* event == holder means we are referenced through the in event holder */
     349         [ #  # ]:          0 :         if (old_holder != &old_event->holder)
     350                 :            :                 fsnotify_destroy_event_holder(old_holder);
     351                 :            : 
     352                 :            :         fsnotify_get_event(new_event); /* on the list take reference */
     353                 :          0 :         fsnotify_put_event(old_event); /* off the list, drop reference */
     354                 :            : 
     355                 :          0 :         return 0;
     356                 :            : }
     357                 :            : 
     358                 :          0 : struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event)
     359                 :            : {
     360                 :            :         struct fsnotify_event *event;
     361                 :            : 
     362                 :          0 :         event = kmem_cache_alloc(fsnotify_event_cachep, GFP_KERNEL);
     363         [ #  # ]:          0 :         if (!event)
     364                 :            :                 return NULL;
     365                 :            : 
     366                 :            :         pr_debug("%s: old_event=%p new_event=%p\n", __func__, old_event, event);
     367                 :            : 
     368                 :          0 :         memcpy(event, old_event, sizeof(*event));
     369                 :            :         initialize_event(event);
     370                 :            : 
     371         [ #  # ]:          0 :         if (event->name_len) {
     372                 :          0 :                 event->file_name = kstrdup(old_event->file_name, GFP_KERNEL);
     373         [ #  # ]:          0 :                 if (!event->file_name) {
     374                 :          0 :                         kmem_cache_free(fsnotify_event_cachep, event);
     375                 :          0 :                         return NULL;
     376                 :            :                 }
     377                 :            :         }
     378                 :          0 :         event->tgid = get_pid(old_event->tgid);
     379         [ #  # ]:          0 :         if (event->data_type == FSNOTIFY_EVENT_PATH)
     380                 :          0 :                 path_get(&event->path);
     381                 :            : 
     382                 :          0 :         return event;
     383                 :            : }
     384                 :            : 
     385                 :            : /*
     386                 :            :  * fsnotify_create_event - Allocate a new event which will be sent to each
     387                 :            :  * group's handle_event function if the group was interested in this
     388                 :            :  * particular event.
     389                 :            :  *
     390                 :            :  * @to_tell the inode which is supposed to receive the event (sometimes a
     391                 :            :  *      parent of the inode to which the event happened.
     392                 :            :  * @mask what actually happened.
     393                 :            :  * @data pointer to the object which was actually affected
     394                 :            :  * @data_type flag indication if the data is a file, path, inode, nothing...
     395                 :            :  * @name the filename, if available
     396                 :            :  */
     397                 :          0 : struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, void *data,
     398                 :            :                                              int data_type, const unsigned char *name,
     399                 :            :                                              u32 cookie, gfp_t gfp)
     400                 :            : {
     401                 :            :         struct fsnotify_event *event;
     402                 :            : 
     403                 :      60277 :         event = kmem_cache_zalloc(fsnotify_event_cachep, gfp);
     404            [ + ]:      60280 :         if (!event)
     405                 :            :                 return NULL;
     406                 :            : 
     407                 :            :         pr_debug("%s: event=%p to_tell=%p mask=%x data=%p data_type=%d\n",
     408                 :            :                  __func__, event, to_tell, mask, data, data_type);
     409                 :            : 
     410                 :            :         initialize_event(event);
     411                 :            : 
     412         [ +  + ]:      60281 :         if (name) {
     413                 :      60264 :                 event->file_name = kstrdup(name, gfp);
     414         [ -  + ]:      60267 :                 if (!event->file_name) {
     415                 :          0 :                         kmem_cache_free(fsnotify_event_cachep, event);
     416                 :          0 :                         return NULL;
     417                 :            :                 }
     418                 :      60267 :                 event->name_len = strlen(event->file_name);
     419                 :            :         }
     420                 :            : 
     421                 :     120555 :         event->tgid = get_pid(task_tgid(current));
     422                 :      60271 :         event->sync_cookie = cookie;
     423                 :      60271 :         event->to_tell = to_tell;
     424                 :      60271 :         event->data_type = data_type;
     425                 :            : 
     426   [ +  +  +  - ]:     120548 :         switch (data_type) {
     427                 :            :         case FSNOTIFY_EVENT_PATH: {
     428                 :            :                 struct path *path = data;
     429                 :      27531 :                 event->path.dentry = path->dentry;
     430                 :      27531 :                 event->path.mnt = path->mnt;
     431                 :      27531 :                 path_get(&event->path);
     432                 :      27582 :                 break;
     433                 :            :         }
     434                 :            :         case FSNOTIFY_EVENT_INODE:
     435                 :      32736 :                 event->inode = data;
     436                 :      32736 :                 break;
     437                 :            :         case FSNOTIFY_EVENT_NONE:
     438                 :          4 :                 event->inode = NULL;
     439                 :          4 :                 event->path.dentry = NULL;
     440                 :            :                 event->path.mnt = NULL;
     441                 :          4 :                 break;
     442                 :            :         default:
     443                 :          0 :                 BUG();
     444                 :            :         }
     445                 :            : 
     446                 :      60322 :         event->mask = mask;
     447                 :            : 
     448                 :      60322 :         return event;
     449                 :            : }
     450                 :            : 
     451                 :          0 : static __init int fsnotify_notification_init(void)
     452                 :            : {
     453                 :          0 :         fsnotify_event_cachep = KMEM_CACHE(fsnotify_event, SLAB_PANIC);
     454                 :          0 :         fsnotify_event_holder_cachep = KMEM_CACHE(fsnotify_event_holder, SLAB_PANIC);
     455                 :            : 
     456                 :          0 :         q_overflow_event = fsnotify_create_event(NULL, FS_Q_OVERFLOW, NULL,
     457                 :            :                                                  FSNOTIFY_EVENT_NONE, NULL, 0,
     458                 :            :                                                  GFP_KERNEL);
     459         [ #  # ]:          0 :         if (!q_overflow_event)
     460                 :          0 :                 panic("unable to allocate fsnotify q_overflow_event\n");
     461                 :            : 
     462                 :          0 :         return 0;
     463                 :            : }
     464                 :            : subsys_initcall(fsnotify_notification_init);

Generated by: LCOV version 1.9