Filemap: add debugging facility
authorArtem Bityutskiy <artem.bityutskiy@intel.com>
Tue, 21 Jan 2014 12:25:08 +0000 (14:25 +0200)
committerArtem Bityutskiy <artem.bityutskiy@intel.com>
Tue, 21 Jan 2014 17:16:23 +0000 (19:16 +0200)
Add the 'log' parameter to the Filemap* classes to allow passing the logger
object where the debugging prints will go. This is similar to what we have in
the BmapCopy module. Also add the same parameter to 'BmapCreate' and make sure
that 'bmaptool' passes its logger to 'BmapCreate', which then passes it to
'Filemap*' objects, where we use it for debugging.

This makes sure that '--debug' triggers debugging messages from 'Filemap*'
ojects.

Change-Id: Idc9686edc4809856720481f79e37887d29a8b0df
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@intel.com>
bmaptool
bmaptools/BmapCreate.py
bmaptools/Filemap.py

index 0c6a3498e9b4b0679979262548602b9579badfd2..5214a836e9ed6363245571754ba9abbd9187d41b 100755 (executable)
--- a/bmaptool
+++ b/bmaptool
@@ -555,7 +555,7 @@ def create_command(args, log):
             raise SystemExit(1)
 
     try:
-        creator = BmapCreate.BmapCreate(args.image, output, "sha256")
+        creator = BmapCreate.BmapCreate(args.image, output, "sha256", log)
         creator.generate(not args.no_checksum)
     except BmapCreate.Error as err:
         log.error(str(err))
index 6e9e2f9feefa6f7b32112b465c4db8f250cec3b7..b70fd73de82462ff91b390f1c6860d5128fa4081 100644 (file)
@@ -43,6 +43,7 @@ This module uses the FIBMAP ioctl to detect holes.
 # pylint: disable=R0902,R0903
 
 import hashlib
+import logging
 from bmaptools.BmapHelpers import human_size
 from bmaptools import Filemap
 
@@ -117,7 +118,7 @@ class BmapCreate(object):
     the FIEMAP ioctl to generate the bmap.
     """
 
-    def __init__(self, image, bmap, chksum_type="sha256"):
+    def __init__(self, image, bmap, chksum_type="sha256", log=None):
         """
         Initialize a class instance:
         * image  - full path or a file-like object of the image to create bmap
@@ -126,8 +127,13 @@ class BmapCreate(object):
                    bmap to
         * chksum - type of the check sum to use in the bmap file (all checksum
                    types which python's "hashlib" module supports are allowed).
+        * log     - the logger object to use for printing messages.
         """
 
+        self._log = log
+        if self._log is None:
+            self._log = logging.getLogger(__name__)
+
         self.image_size = None
         self.image_size_human = None
         self.block_size = None
@@ -165,7 +171,7 @@ class BmapCreate(object):
             self._bmap_path = bmap
             self._open_bmap_file()
 
-        self.filemap = Filemap.filemap(self._f_image)
+        self.filemap = Filemap.filemap(self._f_image, self._log)
 
         self.image_size = self.filemap.image_size
         self.image_size_human = human_size(self.image_size)
index 2c120bf0d687027fbd283feaed3dce5a075ea205..94504f9d6133f59a21d549b500e9c706a2a4a35d 100644 (file)
@@ -27,6 +27,7 @@ import struct
 import array
 import fcntl
 import tempfile
+import logging
 from bmaptools import BmapHelpers
 
 
@@ -46,15 +47,20 @@ class _FilemapBase(object):
     """
     This is a base class for a couple of other classes in this module. This
     class simply performs the common parts of the initialization process: opens
-    the image file, gets its size, etc.
+    the image file, gets its size, etc. The 'log' parameter is the logger object
+    to use for printing messages.
     """
 
-    def __init__(self, image):
+    def __init__(self, image, log=None):
         """
         Initialize a class instance. The 'image' argument is full path to the
         file or file object to operate on.
         """
 
+        self._log = log
+        if self._log is None:
+            self._log = logging.getLogger(__name__)
+
         self._f_image_needs_close = False
 
         if hasattr(image, "fileno"):
@@ -91,6 +97,10 @@ class _FilemapBase(object):
             raise Error("cannot synchronize image file '%s': %s "
                         % (self._image_path, err.strerror))
 
+        self._log.debug("opened image \"%s\"" % self._image_path)
+        self._log.debug("block size %d, blocks count %d, image size %d"
+                        % (self.block_size, self.blocks_cnt, self.image_size))
+
     def __del__(self):
         """The class destructor which just closes the image file."""
         if self._f_image_needs_close:
@@ -158,11 +168,12 @@ class FilemapSeek(_FilemapBase):
     access to the image file.
     """
 
-    def __init__(self, image):
+    def __init__(self, image, log=None):
         """Refer the '_FilemapBase' class for the documentation."""
 
         # Call the base class constructor first
-        _FilemapBase.__init__(self, image)
+        _FilemapBase.__init__(self, image, log)
+        self._log.debug("FilemapSeek: initializing")
 
         self._probe_seek_hole()
 
@@ -198,6 +209,7 @@ class FilemapSeek(_FilemapBase):
         if offs != 0:
             # We are dealing with the stub 'SEEK_HOLE' implementation which
             # always returns EOF.
+            self._log.debug("lseek(0, SEEK_HOLE) returned %d" % offs)
             raise ErrorNotSupp("the file-system does not support "
                                "\"SEEK_HOLE\" and \"SEEK_DATA\" but only "
                                "provides a stub implementation")
@@ -228,9 +240,13 @@ class FilemapSeek(_FilemapBase):
         """Refer the '_FilemapBase' class for the documentation."""
         offs = self._lseek(self._f_image, block * self.block_size, _SEEK_DATA)
         if offs == -1:
-            return False
+            result = False
+        else:
+            result = (offs / self.block_size == block)
 
-        return offs / self.block_size == block
+        self._log.debug("FilemapSeek: block_is_mapped(%d) returns %s"
+                        % (block, result))
+        return result
 
     def block_is_unmapped(self, block):
         """Refer the '_FilemapBase' class for the documentation."""
@@ -258,14 +274,22 @@ class FilemapSeek(_FilemapBase):
             if end > limit:
                 end = limit
 
-            yield (start / self.block_size, end / self.block_size - 1)
+            start_blk = start / self.block_size
+            end_blk = end / self.block_size - 1
+            self._log.debug("FilemapSeek: yielding range (%d, %d)"
+                            % (start_blk, end_blk))
+            yield (start_blk, end_blk)
 
     def get_mapped_ranges(self, start, count):
         """Refer the '_FilemapBase' class for the documentation."""
+        self._log.debug("FilemapSeek: get_mapped_ranges(%d,  %d(%d))"
+                        % (start, count, start + count - 1))
         return self._get_ranges(start, count, _SEEK_DATA, _SEEK_HOLE)
 
     def get_unmapped_ranges(self, start, count):
         """Refer the '_FilemapBase' class for the documentation."""
+        self._log.debug("FilemapSeek: get_unmapped_ranges(%d,  %d(%d))"
+                        % (start, count, start + count - 1))
         return self._get_ranges(start, count, _SEEK_HOLE, _SEEK_DATA)
 
 
@@ -301,14 +325,15 @@ class FilemapFiemap(_FilemapBase):
     ioctl in order to work-around early FIEMAP implementation kernel bugs.
     """
 
-    def __init__(self, image):
+    def __init__(self, image, log=None):
         """
         Initialize a class instance. The 'image' argument is full the file
         object to operate on.
         """
 
         # Call the base class constructor first
-        _FilemapBase.__init__(self, image)
+        _FilemapBase.__init__(self, image, log)
+        self._log.debug("FilemapFiemap: initializing")
 
         self._buf_size = _FIEMAP_BUFFER_SIZE
 
@@ -354,11 +379,15 @@ class FilemapFiemap(_FilemapBase):
             # Note, the FIEMAP ioctl is supported by the Linux kernel starting
             # from version 2.6.28 (year 2008).
             if err.errno == os.errno.EOPNOTSUPP:
-                raise ErrorNotSupp("the FIEMAP ioctl is not supported by "
-                                   "the file-system")
+                errstr = "FilemapFiemap: the FIEMAP ioctl is not supported " \
+                         "by the file-system"
+                self._log.debug(errstr)
+                raise ErrorNotSupp(errstr)
             if err.errno == os.errno.ENOTTY:
-                raise ErrorNotSupp("the FIEMAP ioctl is not supported by "
-                                   "the kernel")
+                errstr = "FilemapFiemap: the FIEMAP ioctl is not supported " \
+                         "by the kernel"
+                self._log.debug(errstr)
+                raise ErrorNotSupp(errstr)
             raise Error("the FIEMAP ioctl failed for '%s': %s"
                         % (self._image_path, err))
 
@@ -371,7 +400,10 @@ class FilemapFiemap(_FilemapBase):
         # The 3rd element of 'struct_fiemap' is the 'fm_mapped_extents' field.
         # If it contains zero, the block is not mapped, otherwise it is
         # mapped.
-        return bool(struct_fiemap[3])
+        result = bool(struct_fiemap[3])
+        self._log.debug("FilemapFiemap: block_is_mapped(%d) returns %s"
+                        % (block, result))
+        return result
 
     def block_is_unmapped(self, block):
         """Refer the '_FilemapBase' class for the documentation."""
@@ -435,33 +467,44 @@ class FilemapFiemap(_FilemapBase):
 
     def get_mapped_ranges(self, start, count):
         """Refer the '_FilemapBase' class for the documentation."""
+        self._log.debug("FilemapFiemap: get_mapped_ranges(%d,  %d(%d))"
+                        % (start, count, start + count - 1))
         iterator = self._do_get_mapped_ranges(start, count)
-
         first_prev, last_prev = iterator.next()
 
         for first, last in iterator:
             if last_prev == first - 1:
                 last_prev = last
             else:
+                self._log.debug("FilemapFiemap: yielding range (%d, %d)"
+                                % (first_prev, last_prev))
                 yield (first_prev, last_prev)
                 first_prev, last_prev = first, last
 
+        self._log.debug("FilemapFiemap: yielding range (%d, %d)"
+                        % (first_prev, last_prev))
         yield (first_prev, last_prev)
 
     def get_unmapped_ranges(self, start, count):
         """Refer the '_FilemapBase' class for the documentation."""
+        self._log.debug("FilemapFiemap: get_unmapped_ranges(%d,  %d(%d))"
+                        % (start, count, start + count - 1))
         hole_first = start
         for first, last in self._do_get_mapped_ranges(start, count):
             if first > hole_first:
+                self._log.debug("FilemapFiemap: yielding range (%d, %d)"
+                                % (hole_first, first - 1))
                 yield (hole_first, first - 1)
 
             hole_first = last + 1
 
         if hole_first < start + count:
+            self._log.debug("FilemapFiemap: yielding range (%d, %d)"
+                            % (hole_first, start + count - 1))
             yield (hole_first, start + count - 1)
 
 
-def filemap(image):
+def filemap(image, log=None):
     """
     Create and return an instance of a Filemap class - 'FilemapFiemap' or
     'FilemapSeek', depending on what the system we run on supports. If the
@@ -472,6 +515,6 @@ def filemap(image):
     """
 
     try:
-        return FilemapFiemap(image)
+        return FilemapFiemap(image, log)
     except ErrorNotSupp:
-        return FilemapSeek(image)
+        return FilemapSeek(image, log)