192 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
| # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
 | |
| # Use of this source code is governed by a BSD-style license that can be
 | |
| # found in the LICENSE file.
 | |
| 
 | |
| import logging, re, subprocess, threading
 | |
| import common
 | |
| from autotest_lib.client.bin import test, utils
 | |
| from autotest_lib.client.common_lib import error
 | |
| 
 | |
| 
 | |
| class hardware_Badblocks(test.test):
 | |
|     """
 | |
|     Runs badblocks on the root partition that is not being used.
 | |
| 
 | |
|     """
 | |
| 
 | |
|     version = 1
 | |
| 
 | |
|     # Define output that is expected from a successful badblocks run.
 | |
|     _EXPECTED_BADBLOCKS_OUTPUT = (
 | |
|             'Pass completed, 0 bad blocks found. (0/0/0 errors)')
 | |
| 
 | |
|     # Define Linux badblocks utility name.
 | |
|     _BADBLOCKS = 'badblocks'
 | |
| 
 | |
|     # Define variables to store some statistics of the runs.
 | |
|     _pass_count = 0
 | |
|     _fail_count = 0
 | |
|     _longest_runtime = 0
 | |
| 
 | |
| 
 | |
|     def _get_sector_size(self, dev):
 | |
|         """
 | |
|         Finds block device's sector size in bytes.
 | |
| 
 | |
|         @return the sector size.
 | |
| 
 | |
|         """
 | |
| 
 | |
|         argv = ('parted ' + dev + ' print | grep "Sector size" | awk -F ' +
 | |
|                 '"/" \'{print $3}\' | sed \'$s/.$//\'')
 | |
| 
 | |
|         return utils.system_output(argv)
 | |
| 
 | |
| 
 | |
|     def _timeout(self, badblocks_proc):
 | |
|         """
 | |
|         Timeout callback for the badblocks process.
 | |
| 
 | |
|         Kills badblocks process if still running and fails test.
 | |
| 
 | |
|         """
 | |
| 
 | |
|         # Kill badblocks, report if not killed, log any exceptions.
 | |
|         if badblocks_proc.poll() == None:
 | |
|             try:
 | |
|                 logging.info('badblocks taking too long---sending SIGKILL')
 | |
|                 badblocks_proc.kill()
 | |
|             except Exception as e:
 | |
|                 logging.info('%s', e)
 | |
|             finally:
 | |
|                 # name of the kernel function in which the process is sleeping.
 | |
|                 argv = ('ps eopid,fname,wchan | grep ' + self._BADBLOCKS +
 | |
|                         ' | awk \'{print $3}\'')
 | |
|                 waiton = utils.system_output(argv)
 | |
|                 if waiton:
 | |
|                     logging.info('badblocks is waiting on %s', waiton)
 | |
|                 raise error.TestError('Error: badblocks timed out.')
 | |
| 
 | |
| 
 | |
|     def _run_badblocks(self, dev, sector_size, tmout):
 | |
|         """
 | |
|         Runs badblocks.
 | |
| 
 | |
|         """
 | |
| 
 | |
|         # Run badblocks on the selected partition, with parameters:
 | |
|         # -s = show progress
 | |
|         # -v = verbose (print error count)
 | |
|         # -w = destructive write+read test
 | |
|         # -b = block size (set equal to sector size)
 | |
|         argv = [self._BADBLOCKS, '-svw', '-d', str(sector_size), dev]
 | |
|         msg = 'Running: ' + ' '.join(map(str, argv))
 | |
|         logging.info(msg)
 | |
|         badblocks_proc = subprocess.Popen(
 | |
|                 argv,
 | |
|                 shell=False,
 | |
|                 stderr=subprocess.STDOUT, # Combine stderr with stdout.
 | |
|                 stdout=subprocess.PIPE)
 | |
| 
 | |
|         # Start timeout timer thread.
 | |
|         t = threading.Timer(tmout, self._timeout, [badblocks_proc])
 | |
|         t.start()
 | |
| 
 | |
|         # Get badblocks output.
 | |
|         stdout, _ = badblocks_proc.communicate()
 | |
| 
 | |
|         # Stop timer if badblocks has finished.
 | |
|         t.cancel()
 | |
| 
 | |
|         # Check badblocks exit status.
 | |
|         if badblocks_proc.returncode != 0:
 | |
|             raise error.TestError('badblocks returned with code: %s',
 | |
|                                   badblocks_proc.returncode)
 | |
| 
 | |
|         # Parse and log badblocks output.
 | |
|         logging.info('badblocks output:')
 | |
|         lines = stdout.split('\n')
 | |
|         del lines[-1] # Remove blank line at end.
 | |
|         logging.info(lines[0])
 | |
|         logging.info(lines[1])
 | |
|         # Log the progress of badblocks (line 2 onwards, minus last line).
 | |
|         for line in lines[2:-1]:
 | |
|             # replace backspace characters with a newline character.
 | |
|             line = re.sub(r'[\b]+', '\n', line)
 | |
|             # Log test pattern info.
 | |
|             pattern_info = line[:line.find(':') + 1]
 | |
|             logging.info('%s', pattern_info)
 | |
|             sublines = line[line.find(':') + 2:].split('\n')
 | |
|             for subline in sublines:
 | |
|                 logging.info('%s', subline)
 | |
|         # Log result (last line).
 | |
|         logging.info(lines[-1])
 | |
| 
 | |
|         # Get run time in seconds.
 | |
|         min_sec = re.match(r'(\w+):(\w+)', lines[-2].split()[-4])
 | |
|         runtime = int(min_sec.group(1)) * 60 + int(min_sec.group(2))
 | |
| 
 | |
|         # Update longest run time.
 | |
|         if self._longest_runtime < runtime:
 | |
|             self._longest_runtime = runtime
 | |
| 
 | |
|         # Check badblocks result.
 | |
|         result = lines[-1].strip()
 | |
|         if result != self._EXPECTED_BADBLOCKS_OUTPUT:
 | |
|             self._fail_count += 1
 | |
|             return
 | |
|         self._pass_count += 1
 | |
| 
 | |
| 
 | |
|     def run_once(self, iters=1, tmout=60 * 60):
 | |
|         """
 | |
|         Executes test.
 | |
| 
 | |
|         @param iters: Number of times to run badblocks.
 | |
|         @param tmout: Time allowed badblocks to run before killing it.
 | |
|                       (Default time is 60 minutes.)
 | |
| 
 | |
|         """
 | |
| 
 | |
|         # Log starting message.
 | |
|         logging.info('Statring hardware_Badblocks Test.')
 | |
|         logging.info('Iterations: %d', iters)
 | |
|         logging.info('badblocks Timeout (sec): %d', tmout)
 | |
| 
 | |
|         # Determine which device and partition to use.
 | |
|         logging.info('Determine unused root partition to test on:')
 | |
|         dev = utils.get_free_root_partition()
 | |
|         logging.info('Testing on ' + dev)
 | |
| 
 | |
|         # Get block device's sector size.
 | |
|         logging.info('Determine block device sector size:')
 | |
|         sector_size = self._get_sector_size(utils.get_root_device())
 | |
|         logging.info('Sector size (bytes): ' + sector_size)
 | |
| 
 | |
|         # Get partition size.
 | |
|         logging.info('Determine partition size:')
 | |
|         part_size = utils.get_disk_size(dev)
 | |
|         logging.info('Partition size (bytes): %s', part_size)
 | |
| 
 | |
|         # Run badblocks.
 | |
|         for i in range(iters):
 | |
|             logging.info('Starting iteration %d', i)
 | |
|             self._run_badblocks(dev, sector_size, tmout)
 | |
| 
 | |
|         # Report statistics.
 | |
|         logging.info('Total pass: %d', self._pass_count)
 | |
|         logging.info('Total fail: %d', self._fail_count)
 | |
|         stats = {}
 | |
|         stats['ea_badblocks_runs'] = iters
 | |
|         stats['ea_passed_count'] = self._pass_count
 | |
|         stats['ea_failed_count'] = self._fail_count
 | |
|         stats['sec_longest_run'] = self._longest_runtime
 | |
|         # TODO: change write_perf_keyval() to output_perf_value() as soon as
 | |
|         # autotest is ready for it.
 | |
|         self.write_perf_keyval(stats)
 | |
| 
 | |
|         # Report test pass/fail.
 | |
|         if self._pass_count != iters:
 | |
|             raise error.TestFail('One or more runs found bad blocks on'
 | |
|                                  ' storage device.')
 |