LCOV - code coverage report
Current view: top level - tests/harness - fdtracker.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 954b5873a738 Lines: 46 65 70.8 %
Date: 2019-06-30 05:20:33 Functions: 7 7 100.0 %
Branches: 25 80 31.2 %

           Branch data     Line data    Source code
       1                 :            : /** @file fdtracker.cc
       2                 :            :  * @brief Track leaked file descriptors.
       3                 :            :  */
       4                 :            : /* Copyright (C) 2010,2014,2015,2018 Olly Betts
       5                 :            :  *
       6                 :            :  * This program is free software; you can redistribute it and/or
       7                 :            :  * modify it under the terms of the GNU General Public License as
       8                 :            :  * published by the Free Software Foundation; either version 2 of the
       9                 :            :  * License, or (at your option) any later version.
      10                 :            :  *
      11                 :            :  * This program is distributed in the hope that it will be useful,
      12                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14                 :            :  * GNU General Public License for more details.
      15                 :            :  *
      16                 :            :  * You should have received a copy of the GNU General Public License
      17                 :            :  * along with this program; if not, write to the Free Software
      18                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
      19                 :            :  */
      20                 :            : 
      21                 :            : #include <config.h>
      22                 :            : 
      23                 :            : #include "fdtracker.h"
      24                 :            : 
      25                 :            : #ifdef XAPIAN_TESTSUITE_TRACK_FDS
      26                 :            : 
      27                 :            : #include "safeunistd.h"
      28                 :            : #include "safedirent.h"
      29                 :            : 
      30                 :            : #include <iostream>
      31                 :            : #include <cerrno>
      32                 :            : #include <cstdlib>
      33                 :            : #include <cstring> // For memcmp().
      34                 :            : #include <set>
      35                 :            : 
      36                 :            : #include "errno_to_string.h"
      37                 :            : #include "str.h"
      38                 :            : #include "stringutils.h"
      39                 :            : 
      40                 :            : using namespace std;
      41                 :            : 
      42                 :            : // Directory to try to read open fds from.  If this directory doesn't exist
      43                 :            : // then fd tracking will just be disabled.  It seems "/dev/fd" is the more
      44                 :            : // common name for this.  On Linux and Cygwin, "/dev/fd" is usually a symlink
      45                 :            : // to "/proc/self/fd", but that symlink can sometimes be missing so prefer
      46                 :            : // the latter on these platforms.
      47                 :            : #if defined __linux__ || defined __CYGWIN__
      48                 :            : # define FD_DIRECTORY "/proc/self/fd"
      49                 :            : #else
      50                 :            : # define FD_DIRECTORY "/dev/fd"
      51                 :            : #endif
      52                 :            : 
      53                 :            : void
      54                 :      17167 : FDTracker::mark_fd(int fd)
      55                 :            : {
      56         [ +  - ]:      17167 :     if (fd >= 0) {
      57         [ +  + ]:      17167 :         if (size_t(fd) >= fds.size())
      58                 :       2607 :             fds.resize((fd &~ 31) + 32);
      59                 :      17167 :         fds[fd] = true;
      60                 :            :     }
      61                 :      17167 : }
      62                 :            : 
      63                 :            : bool
      64                 :      16935 : FDTracker::check_fd(int fd) const
      65                 :            : {
      66 [ +  - ][ +  - ]:      16935 :     return size_t(fd) < fds.size() && fds[fd];
      67                 :            : }
      68                 :            : 
      69                 :       5214 : FDTracker::~FDTracker()
      70                 :            : {
      71         [ +  - ]:       2607 :     if (dir_void) {
      72                 :       2607 :         DIR * dir = static_cast<DIR*>(dir_void);
      73                 :       2607 :         closedir(dir);
      74                 :            :     }
      75                 :       2607 : }
      76                 :            : 
      77                 :            : void
      78                 :       2607 : FDTracker::init()
      79                 :            : {
      80                 :       2607 :     DIR * dir = opendir(FD_DIRECTORY);
      81                 :            :     // Not all platforms have such a directory.
      82         [ -  + ]:       5214 :     if (!dir) return;
      83                 :       2607 :     dir_void = static_cast<void*>(dir);
      84                 :            : 
      85                 :            :     // The list of fds we get will include the fd inside dir, but that's OK as
      86                 :            :     // we keep dir open while the testcase runs and use it to recheck the open
      87                 :            :     // fds at the end.
      88                 :            :     while (true) {
      89                 :      24988 :         errno = 0;
      90                 :      24988 :         struct dirent * entry = readdir(dir);
      91         [ +  + ]:      24988 :         if (!entry) {
      92         [ +  - ]:       2607 :             if (errno == 0)
      93                 :       2607 :                 break;
      94 [ #  # ][ #  # ]:          0 :             cout << "readdir failed: " << errno_to_string(errno) << endl;
                 [ #  # ]
      95                 :          0 :             exit(1);
      96                 :            :         }
      97                 :            : 
      98                 :      22381 :         const char * name = entry->d_name;
      99                 :            : 
     100                 :            :         // Ignore at least '.' and '..'.
     101 [ +  + ][ -  + ]:      22381 :         if (name[0] < '0' || name[0] > '9')
     102                 :       5214 :             continue;
     103                 :            : 
     104                 :      17167 :         mark_fd(atoi(name));
     105                 :      22381 :     }
     106                 :            : }
     107                 :            : 
     108                 :            : bool
     109                 :       2568 : FDTracker::check()
     110                 :            : {
     111                 :       2568 :     bool ok = true;
     112                 :       2568 :     DIR * dir = static_cast<DIR*>(dir_void);
     113         [ -  + ]:       2568 :     if (!dir) return true;
     114                 :       2568 :     rewinddir(dir);
     115                 :            : 
     116                 :       2568 :     message.resize(0);
     117                 :            : 
     118                 :            :     while (true) {
     119                 :      24639 :         errno = 0;
     120         [ +  - ]:      24639 :         struct dirent * entry = readdir(dir);
     121         [ +  + ]:      24639 :         if (!entry) {
     122         [ +  - ]:       2568 :             if (errno == 0)
     123                 :       2568 :                 break;
     124 [ #  # ][ #  # ]:          0 :             cout << "readdir failed: " << errno_to_string(errno) << endl;
         [ #  # ][ #  # ]
     125                 :          0 :             exit(1);
     126                 :            :         }
     127                 :            : 
     128                 :      22071 :         const char * name = entry->d_name;
     129                 :            : 
     130                 :            :         // Ignore at least '.' and '..'.
     131 [ +  + ][ -  + ]:      22071 :         if (name[0] < '0' || name[0] > '9')
     132                 :      22071 :             continue;
     133                 :            : 
     134                 :      16935 :         int fd = atoi(name);
     135 [ +  - ][ +  - ]:      16935 :         if (check_fd(fd)) {
     136                 :            :             // This fd was already open before the testcase.
     137                 :      16935 :             continue;
     138                 :            :         }
     139                 :            : 
     140         [ #  # ]:          0 :         string proc_symlink = FD_DIRECTORY "/";
     141         [ #  # ]:          0 :         proc_symlink += name;
     142                 :            : 
     143                 :            :         char buf[1024];
     144                 :            :         // On some systems (Solaris, AIX) the entries aren't symlinks, so
     145                 :            :         // don't complain if readlink() fails.
     146                 :          0 :         int res = readlink(proc_symlink.c_str(), buf, sizeof(buf));
     147 [ #  # ][ #  # ]:          0 :         if (res == CONST_STRLEN("/dev/urandom") &&
     148                 :          0 :             memcmp(buf, "/dev/urandom", CONST_STRLEN("/dev/urandom")) == 0) {
     149                 :            :             // /dev/urandom isn't a real leak - something in the C library
     150                 :            :             // opens it lazily (at least on Linux).
     151         [ #  # ]:          0 :             mark_fd(fd);
     152                 :          0 :             continue;
     153                 :            :         }
     154                 :            : 
     155         [ #  # ]:          0 :         message += ' ';
     156 [ #  # ][ #  # ]:          0 :         message += str(fd);
     157         [ #  # ]:          0 :         if (res > 0) {
     158         [ #  # ]:          0 :             message += " -> ";
     159         [ #  # ]:          0 :             message.append(buf, res);
     160                 :            :         }
     161                 :            : 
     162                 :            :         // Mark the leaked fd as used so we don't report it for future tests.
     163         [ #  # ]:          0 :         mark_fd(fd);
     164         [ #  # ]:          0 :         ok = false;
     165                 :          0 :     }
     166                 :      24639 :     return ok;
     167 [ +  - ][ +  - ]:         16 : }
     168                 :            : 
     169                 :            : #endif // XAPIAN_TESTSUITE_TRACK_FDS

Generated by: LCOV version 1.11