Home | History | Annotate | Download | only in hardware_MultiReaderPowerConsumption
      1 import os, logging
      2 
      3 from autotest_lib.client.bin import utils
      4 from autotest_lib.client.common_lib import error, autotemp
      5 from autotest_lib.client.cros import storage as storage_mod
      6 from autotest_lib.client.cros.power import power_status
      7 
      8 
      9 class hardware_MultiReaderPowerConsumption(storage_mod.StorageTester):
     10     version = 1
     11     _files_to_delete = []
     12     _ramdisk_path = None
     13     _storage = None
     14 
     15 
     16     def initialize(self):
     17         super(hardware_MultiReaderPowerConsumption, self).initialize()
     18 
     19         # Make sure we're not on AC power
     20         self.status = power_status.get_status()
     21         if self.status.on_ac():
     22             raise error.TestNAError(
     23                   'This test needs to be run with the AC power offline')
     24 
     25 
     26     def cleanup(self):
     27         # Remove intermediate files
     28         for path in self._files_to_delete:
     29             utils.system('rm -f %s' % path)
     30 
     31         if self._storage and os.path.ismount(self._storage['mountpoint']):
     32             self.scanner.umount_volume(storage_dict=self._storage)
     33 
     34         if self._ramdisk_path and os.path.ismount(self._ramdisk_path.name):
     35             umount_ramdisk(self._ramdisk_path.name)
     36             self._ramdisk_path.clean()
     37 
     38         super(hardware_MultiReaderPowerConsumption, self).cleanup()
     39 
     40 
     41     def readwrite_test(self, path, size, delete_file=False):
     42         """Heavy-duty random read/write test. Run `dd` & `tail -f` in parallel
     43 
     44         The random write is done by writing a file from /dev/urandom into the
     45         given location, while the random read is done by concurrently reading
     46         that file.
     47 
     48         @param path: The directory that will create the test file.
     49         @param size: Size of the test file, in MiB.
     50         @param delete_file: Flag the file to be deleted on test exit.
     51                Otherwise file deletion won't be performed.
     52         """
     53         # Calculate the parameters for dd
     54         size = 1024*1024*size
     55         blocksize = 8192
     56 
     57         # Calculate the filename and full path, flag to delete if needed
     58         filename = 'tempfile.%d.delete-me' % size
     59         pathfile = os.path.join(path, filename)
     60         if delete_file:
     61             self._files_to_delete.append(pathfile)
     62 
     63         pid = os.fork() # We need to run two processes in parallel
     64         if pid:
     65             # parent
     66             utils.BgJob('tail -f %s --pid=%s > /dev/null'
     67                         % (pathfile, pid))
     68             # Reap the dd child so that tail does not wait for the zombie
     69             os.waitpid(pid, 0)
     70         else:
     71             # child
     72             utils.system('dd if=/dev/urandom of=%s bs=%d count=%s'
     73                          % (pathfile, blocksize, (size//blocksize)))
     74             # A forked child is exiting here, so we really do want os._exit:
     75             os._exit(0)
     76 
     77 
     78     def run_once(self, ramdisk_size=513, file_size=512, drain_limit=1.05,
     79                  volume_filter={'bus': 'usb'}):
     80         """Test card reader CPU power consumption to be within acceptable
     81         range while performing random r/w
     82 
     83         The random r/w is performed in the readwrite_test() method, by
     84         concurrently running `dd if=/dev/urandom` and `tail -f`. It is run once
     85         on a ramdisk with the SD card mounted, then on the SD card with the
     86         ramdisk unmounted, and then on the SD card with the ramdisk unmounted.
     87         The measured values are then reported.
     88 
     89         @param ramdisk_size: Size of ramdisk (in MiB).
     90         @param file_size: Size of test file (in MiB).
     91         @param volume_filter: Where to find the card reader.
     92         @param drain_limit: maximum ratio between the card reader
     93                             energy consumption and each of the two
     94                             ramdisk read/write test energy consumption
     95                             values. 1.00 means the card reader test may
     96                             not consume more energy than either ramdisk
     97                             test, 0.9 means it may consume no more than
     98                             90% of the ramdisk value, and so forth.
     99         """
    100         # Switch to VT2 so the screen turns itself off automatically instead of
    101         # dimming, in order to reduce the battery consuption caused by other
    102         # variables.
    103         utils.system('chvt 2')
    104 
    105         logging.debug('STEP 1: ensure SD card is inserted and mounted')
    106         self._storage = self.wait_for_device(volume_filter, cycles=1,
    107                                              mount_volume=True)[0]
    108 
    109         logging.debug('STEP 2: mount the ramdisk')
    110         self._ramdisk_path = autotemp.tempdir(unique_id='ramdisk',
    111                                               dir=self.tmpdir)
    112         mount_ramdisk(self._ramdisk_path.name, ramdisk_size)
    113 
    114         # Read current charge, as well as maximum charge.
    115         self.status.refresh()
    116         max_charge = self.status.battery[0].charge_full_design
    117         initial_charge = self.status.battery[0].charge_now
    118 
    119         logging.debug('STEP 3: perform heavy-duty read-write test on ramdisk')
    120         self.readwrite_test(self._ramdisk_path.name, file_size)
    121         # Read current charge (reading A)
    122         self.status.refresh()
    123         charge_A = self.status.battery[0].charge_now
    124 
    125         logging.debug('STEP 4: unmount ramdisk')
    126         umount_ramdisk(self._ramdisk_path.name)
    127 
    128         logging.debug('STEP 5: perform identical read write test on SD card')
    129         self.readwrite_test(self._storage['mountpoint'], file_size,
    130                             delete_file=True)
    131         # Read current charge (reading B)
    132         self.status.refresh()
    133         charge_B = self.status.battery[0].charge_now
    134 
    135         logging.debug('STEP 6: unmount card')
    136         self.scanner.umount_volume(storage_dict=self._storage, args='-f -l')
    137 
    138         logging.debug('STEP 7: perform ramdisk test again')
    139         mount_ramdisk(self._ramdisk_path.name, ramdisk_size)
    140         self.readwrite_test(self._ramdisk_path.name, file_size)
    141         # Read current charge (reading C)
    142         self.status.refresh()
    143         charge_C = self.status.battery[0].charge_now
    144 
    145         # Compute the results
    146         ramdisk_plus = initial_charge - charge_A
    147         sd_card_solo = charge_A - charge_B
    148         ramdisk_solo = charge_B - charge_C
    149 
    150         sd_card_drain_ratio_a = (sd_card_solo / ramdisk_plus)
    151         sd_card_drain_ratio_b = (sd_card_solo / ramdisk_solo)
    152 
    153         msg = None
    154         if sd_card_drain_ratio_a > drain_limit:
    155             msg = ('Card reader drain exceeds mounted baseline by > %f (%f)'
    156                    % (drain_limit, sd_card_drain_ratio_a))
    157         elif sd_card_drain_ratio_b > drain_limit:
    158             msg = ('Card reader drain exceeds unmounted baseline by > %f (%f)'
    159                    % (drain_limit, sd_card_drain_ratio_b))
    160 
    161         if msg:
    162             raise error.TestError(msg)
    163         else:
    164             fmt = 'Card reader drain ratio Ok: mounted %f; unmounted %f'
    165             logging.info(fmt % (sd_card_drain_ratio_a, sd_card_drain_ratio_b))
    166 
    167 
    168 def mount_ramdisk(path, size):
    169     utils.system('mount -t tmpfs none %s -o size=%sm' % (path, size))
    170 
    171 
    172 def umount_ramdisk(path):
    173     """Umount ramdisk mounted at |path|
    174 
    175     @param path: the mountpoint for the mountd RAM disk
    176     """
    177     utils.system('rm -rf %s/*' % path)
    178     utils.system('umount -f -l %s' % path)
    179