Checking tool for stuck bits
31.03.2021, 21:47 - Autor: Mark B.
Stuck bits may cause very difficult and not obvious to spot issues. To test for such a case I wrote this script...
The idea behind the script ist that in normal data the count of bits containing 0 and 1 should be statistaically arround the 50% mark. Data get internally transferes in many devices on so-called data lanes and if one of then is shorted or broken each bit transfered over that lane is always 0 or 1. So if we spot a 0% or 100% value on a speciffic bit we can be pretty certain that we deal with a "stuck bit".
The following script shows bit statistics for 64, 32, 16, 8 and 4 bit data-lanes:
C:\Users\DISK DOCTOR\Desktop>py.exe -3.8 test.py "Generic Storage Device 0.dsk" 100000
Reading sector no. 10000 of 10000
REPORT FOR 'Generic Storage Device 0.dsk':
Counting bits which are 1
BIT | % | % | % | % | %
-----------------------------------
0 | 50% | 49% | 49% | 49% | 48% |
1 | 49% | 49% | 48% | 48% | 48% |
2 | 0% | 0% | 0% | 0% | 0% |
3 | 49% | 49% | 49% | 49% | 48% |
4 | 49% | 49% | 48% | 48% |
5 | 49% | 49% | 49% | 49% |
6 | 0% | 0% | 0% | 0% |
7 | 48% | 48% | 48% | 48% |
8 | 49% | 49% | 49% |
9 | 49% | 49% | 48% |
10 | 0% | 0% | 0% |
11 | 50% | 50% | 49% |
12 | 48% | 48% | 48% |
13 | 49% | 49% | 48% |
14 | 0% | 0% | 0% |
15 | 48% | 47% | 47% |
16 | 48% | 48% |
17 | 48% | 48% |
18 | 0% | 0% |
19 | 48% | 48% |
20 | 48% | 48% |
21 | 48% | 48% |
22 | 0% | 0% |
23 | 48% | 47% |
24 | 48% | 48% |
25 | 48% | 48% |
26 | 0% | 0% |
27 | 48% | 48% |
28 | 48% | 48% |
29 | 48% | 48% |
30 | 0% | 0% |
31 | 47% | 47% |
32 | 48% |
33 | 49% |
34 | 0% |
35 | 49% |
36 | 49% |
37 | 49% |
38 | 0% |
39 | 48% |
40 | 49% |
41 | 48% |
42 | 0% |
43 | 50% |
44 | 48% |
45 | 49% |
46 | 0% |
47 | 47% |
48 | 48% |
49 | 48% |
50 | 0% |
51 | 48% |
52 | 48% |
53 | 48% |
54 | 0% |
55 | 47% |
56 | 48% |
57 | 48% |
58 | 0% |
59 | 48% |
60 | 48% |
61 | 48% |
62 | 0% |
63 | 47% |
Based on that output we can conclude that we have stucked bits on data-lane 2! The pattern OK, OK, STUCK and OK from the 4 bit example repeats 2x for 8bit, 4x for 16bit, etc.
Source:
import traceback, sys, os
if __name__ == "__main__":
ts = time.time()
def read_file(path, max_sectors = 10000):
sum_bits64 = [0] * 64
sum_bits32 = [0] * 32
sum_bits16 = [0] * 16
sum_bits8 = [0] * 8
sum_bits4 = [0] * 4
ctr = 0
try:
with open(path, "rb") as f:
while True:
sector = f.read(512)
# Skip empty sectors
if sector == b"\x00" * 512:
continue
# End when there is no more data
if sector == b"" or sector == None:
break
ctr += 1
# Read sector and sum up bits
for i in range(0, 512, 8):
byte = sector[i:(i+8)]
try:
bits = [0] * 64
for j in range(8):
for k in range(8):
pos = j * 8 + k
bits[pos] = ((byte[j] >> k) & 1)
except IndexError:
break
sum_bits64 = [x + y for x, y in zip(sum_bits64, bits)]
sum_bits32 = [x + y for x, y in zip(sum_bits64[0:32], sum_bits64[32:])]
sum_bits16 = [x + y for x, y in zip(sum_bits32[0:16], sum_bits32[16:])]
sum_bits8 = [x + y for x, y in zip(sum_bits16[0:8], sum_bits16[8:])]
sum_bits4 = [x + y for x, y in zip(sum_bits8[0:4], sum_bits8[4:])]
# Output progress message
if not ctr % 10:
print(f"Reading sector no. {ctr} of {max_sectors}", end = "\r")
sys.stdout.flush()
# End if max. number of sectors are tested
if ctr >= max_sectors:
break
except FileNotFoundError:
print(f"FILE {path} NOT FOUND!")
return 1
except PermissionError:
print(f"CAN'T ACCESS FILE {path}!")
return 2
# Print report
print(f"\n\nREPORT FOR '{path}':")
print("Counting bits which are 1\n")
line = ""
print("BIT | % | % | % | % | % ")
print("-----------------------------------")
for i in range(64):
res = i
if i < 4:
line = f"{i:3} | "
line += f"{int((sum_bits64[i] * 100) // ((ctr*512)/8)):2}% | "
line += f"{int((sum_bits32[i] * 100) // ((ctr*512)/4)):2}% | "
line += f"{int((sum_bits16[i] * 100) // ((ctr*512)/2)):2}% | "
line += f"{int((sum_bits8[i] * 100) // ((ctr*512))):2}% | "
line += f"{int((sum_bits4[i] * 100) // ((ctr*512)*2)):2}% |"
elif i < 8:
line = f"{i:3} | "
line += f"{int((sum_bits64[i] * 100) // ((ctr*512)/8)):2}% | "
line += f"{int((sum_bits32[i] * 100) // ((ctr*512)/4)):2}% | "
line += f"{int((sum_bits16[i] * 100) // ((ctr*512)/2)):2}% | "
line += f"{int((sum_bits8[i] * 100) // ((ctr*512))):2}% | "
elif i < 16:
line = f"{i:3} | "
line += f"{int((sum_bits64[i] * 100) // ((ctr*512)/8)):2}% | "
line += f"{int((sum_bits32[i] * 100) // ((ctr*512)/4)):2}% | "
line += f"{int((sum_bits16[i] * 100) // ((ctr*512)/2)):2}% | "
elif i < 32:
line = f"{i:3} | "
line += f"{int((sum_bits64[i] * 100) // ((ctr*512)/8)):2}% | "
line += f"{int((sum_bits32[i] * 100) // ((ctr*512)/4)):2}% | "
else:
line = f"{i:3} | "
line += f"{int((sum_bits64[i] * 100) // ((ctr*512)/8)):2}% | "
print(line)
def usage():
print("\nUSAGE:\n------")
print(f"{os.path.basename(sys.argv[0])} /path/to/file.dd [no. of sectors to test]\n")
print(f"e.g.: \n{os.path.basename(sys.argv[0])} C:/Users/DISKDOCTOR/Desktop/file.dd 10000\n")
a = b[4]
quit()
# Check arguments
if "-h" in sys.argv or "--help" in sys.argv or len(sys.argv) < 2 or len(sys.argv) > 3:
usage()
try:
read_file(sys.argv[1], int(sys.argv[2]))
except IndexError:
read_file(sys.argv[1])
except TypeError:
usage()
Download
System requrements:
Windows, OSX or Linux with