random_tester.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. #!/usr/bin/env python3
  2. '''
  3. ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  4. ZuluSCSI™ file is licensed under the GPL version 3 or any later version. 
  5. https://www.gnu.org/licenses/gpl-3.0.html
  6. ----
  7. This program is free software: you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation, either version 3 of the License, or
  10. (at your option) any later version. 
  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. You should have received a copy of the GNU General Public License
  16. along with this program.  If not, see <https://www.gnu.org/licenses/>.
  17. '''
  18. '''This script executes random-sized reads and writes to one or more block devices to test them.
  19. It will destroy the contents of the block device.'''
  20. import sys
  21. import os
  22. import mmap
  23. import random
  24. import time
  25. class BlockDevice:
  26. def __init__(self, path, sectorsize = 512):
  27. self.path = path
  28. self.dev = os.fdopen(os.open(path, os.O_RDWR | os.O_DIRECT | os.O_SYNC), "rb+", 0)
  29. self.sectorsize = sectorsize
  30. def write_block(self, first_sector, sector_count, seed):
  31. rnd = random.Random(seed)
  32. buffer = mmap.mmap(-1, sector_count * self.sectorsize)
  33. buffer.write(rnd.randbytes(sector_count * self.sectorsize))
  34. start = time.time()
  35. self.dev.seek(first_sector * self.sectorsize)
  36. self.dev.write(buffer)
  37. elapsed = time.time() - start
  38. speed = sector_count * self.sectorsize / elapsed / 1e6
  39. print("Wrote %16s, %8d, %8d, %8d, %8.3f MB/s" % (self.path, first_sector, sector_count, seed, speed))
  40. def verify_block(self, first_sector, sector_count, seed):
  41. rnd = random.Random(seed)
  42. buffer = mmap.mmap(-1, sector_count * self.sectorsize)
  43. start = time.time()
  44. self.dev.seek(first_sector * self.sectorsize)
  45. self.dev.readinto(buffer)
  46. elapsed = time.time() - start
  47. speed = sector_count * self.sectorsize / elapsed / 1e6
  48. print("Verify %16s, %8d, %8d, %8d, %8.3f MB/s" % (self.path, first_sector, sector_count, seed, speed))
  49. buffer.seek(0)
  50. actual = buffer.read(sector_count * self.sectorsize)
  51. expected = rnd.randbytes(sector_count * self.sectorsize)
  52. if expected != actual:
  53. print("Compare error, device = %s, sectorsize = %d, first_sector = %d, sector_count = %d, seed = %d"
  54. % (self.path, self.sectorsize, first_sector, sector_count, seed))
  55. fname = "%d" % time.time()
  56. open(fname + ".expected", "wb").write(expected)
  57. open(fname + ".actual", "wb").write(actual)
  58. print("Saved data to %s.expected/actual" % fname)
  59. raise Exception("Compare error")
  60. if __name__ == "__main__":
  61. blockdevs = []
  62. for path in sys.argv[1:]:
  63. sectorsize = 512
  64. if ':' in path:
  65. path, sectorsize = path.split(':')
  66. sectorsize = int(sectorsize)
  67. blockdevs.append(BlockDevice(path, sectorsize=sectorsize))
  68. maxsectors = 100000
  69. rnd = random.Random()
  70. while True:
  71. blocks = []
  72. start = 0
  73. while start + 256 < maxsectors:
  74. start = min(maxsectors, start + rnd.randint(0, 10000))
  75. dev = rnd.choice(blockdevs)
  76. count = rnd.randint(1, 256)
  77. seed = rnd.randint(1, 10000000)
  78. blocks.append((dev, start, count, seed))
  79. start += count
  80. print("Write / verify set size: %d" % len(blocks))
  81. random.shuffle(blocks)
  82. for dev, start, count, seed in blocks:
  83. dev.write_block(start, count, seed)
  84. random.shuffle(blocks)
  85. for dev, start, count, seed in blocks:
  86. dev.verify_block(start, count, seed)