| #!/usr/bin/env python3 |
| """ |
| multidd - run multiple dd concurrently to determine disk throughput |
| |
| Usage: multidd {-i infile} {-o outfile} [-B blocksz] [-S filesz] |
| |
| -i infile where infile specifies the path of input file |
| -o outfile where outfile specifies the path of output file |
| -B blocksz where blocksz is the size of each I/O block |
| [default=8KB] |
| -S filesz where filesz is the size of the file to write |
| [default=2X system RAM] |
| |
| Multiple -i and -o arguments may be specified but they must pair up. |
| |
| e.g. multidd -i /dev/zero -o /dbfast1/ddtest -B 8kb -S 2gb |
| """ |
| |
| import getopt |
| import math |
| import os |
| import sys |
| |
| |
| def usage(exitarg): |
| print(__doc__) |
| sys.exit(exitarg) |
| |
| |
| def run(cmd): |
| f = None |
| ok = False |
| out = None |
| try: |
| f = os.popen(cmd) |
| out = f.read() |
| ok = not f.close() |
| except: |
| f.close() |
| ok = False |
| return ok, out |
| |
| |
| def getPlatform(): |
| if sys.platform.find('linux') >= 0: |
| return 'linux' |
| if sys.platform.find('darwin') >= 0: |
| return 'darwin' |
| return '?' |
| |
| |
| def getMemory(): |
| if getPlatform() == 'linux': |
| ok, out = run("sh -c 'cat /proc/meminfo | grep MemTotal'") |
| if not ok: |
| return '?' |
| mem_string_list = out.strip().split(' ') |
| val = int(mem_string_list[len(mem_string_list) - 2]) |
| factor = mem_string_list[len(mem_string_list) - 1] |
| if factor == 'kB': |
| return val * 1024 |
| return '?' |
| |
| if getPlatform() == 'darwin': |
| ok, out = run("sysctl hw.physmem") |
| if not ok: |
| return '?' |
| mem_string_list = out.strip().split(' ') |
| val = int(mem_string_list[1]) |
| return val |
| |
| return '?' |
| |
| |
| def parseMemorySize(line): |
| factor = 1 |
| try: |
| line = line.strip().upper() |
| if line.endswith('B'): |
| line = line[:-1] |
| if line.endswith('G'): |
| factor = 1024 * 1024 * 1024 |
| elif line.endswith('M'): |
| factor = 1024 * 1024 |
| elif line.endswith('K'): |
| factor = 1024 |
| if factor > 1: |
| line = line[:-1] |
| return int(line) * factor |
| except: |
| return 0 |
| |
| |
| def parseCommandLine(): |
| global opt |
| try: |
| (options, args) = getopt.getopt(sys.argv[1:], '?i:o:B:S:') |
| except: |
| e = sys.exc_info() |
| usage('Error: %s %s' % (e[0], e[1])) |
| |
| for (switch, val) in options: |
| if switch == '-?': |
| usage(0) |
| elif switch[1] in 'io': |
| opt[switch].append(val) |
| elif switch[1] in 'BS': |
| opt[switch] = parseMemorySize(val) |
| |
| if opt['-S'] == '?': |
| sys.exit('Error: unable to obtain system RAM size') |
| if opt['-S'] < 0: |
| usage('Error: invalid -S filesz parameter') |
| if opt['-B'] <= 0: |
| usage('Error: invalid -B blocksz parameter') |
| if opt['-B'] > 1024 * 1024: |
| usage('Error: maximum 1MB for -B parameter') |
| if len(opt['-i']) != len(opt['-o']): |
| usage('Error: -i and -o parameters must pair up') |
| if len(opt['-i']) == 0: |
| usage('Error: missing -i and -o parameters') |
| |
| |
| opt = {'-i': [], '-o': [], '-B': parseMemorySize('8KB'), '-S': 0} |
| parseCommandLine() |
| if opt['-S'] == 0: |
| opt['-S'] = getMemory() * 2 |
| if opt['-S'] < opt['-B']: |
| opt['-S'] = opt['-B'] |
| cmd = [] |
| pfile = [] |
| blocksz = opt['-B'] |
| cnt = int(math.ceil(opt['-S'] / blocksz)) |
| totalBytes = 0 |
| for i in range(len(opt['-i'])): |
| ifile = opt['-i'][i] |
| ofile = opt['-o'][i] |
| cmd.append('dd if=%s of=%s count=%d bs=%d' % (ifile, ofile, cnt, blocksz)) |
| totalBytes += cnt * blocksz |
| |
| for c in cmd: |
| print(c) |
| pfile.append(os.popen(c)) |
| |
| for f in pfile: |
| ok = False |
| try: |
| print(f.read()) |
| ok = not f.close() |
| f = None |
| except: |
| if f: |
| f.close() |
| ok = False |
| |
| if not ok: |
| sys.exit(1) |
| |
| os.system('sync') |
| print('multidd total bytes ', totalBytes) |