Following a discussion on another thread about the life limits of an SDCard I thought I would write a program to write one to death and see just how long it lasts.
I'll post the code below but the general idea is that I have a random number generator that is creating file names. I calculate the number of possible files by taking the Java FileStore usable space, taking 90% of that and dividing it by the file size, 409,600 bytes. That is 10 blocks of 4096 bytes the block size from the Java FileStore. So a randomly selected file name is checked for existence, if it doesn't exist I write
409,600 bytes of 1s to the file. If it does exist and it has 1s in it I write 409,600 0s to the file. If it has 0s in it, I delete it. I do this 1,000,000 times then I delete all the files in the directory and start over. I'm having the program send me statistics every hour so it should be fairly obvious when it dies or the usable space gets really small because of marked off blocks.I'm running some tests now on an old card I had lying around. I am going to order some new cards once I am sure I have the right test going.
I would appreciate comments on the testing algorithm and on my code if you have any.
Thanks,
package com.knutejohnson.pi.killer;
import java.io.*; import java.nio.file.*; import java.time.*; import java.time.format.*; import java.util.*; import java.util.concurrent.*; import java.util.stream.*; import static java.util.stream.Collectors.*; import javax.activation.*; import javax.mail.*; import javax.mail.internet.*; import javax.mail.util.*;
public class SDCardKiller implements Runnable { private static final String dataDir = "/home/pi/bin/files"; private final Random random = new Random(System.currentTimeMillis()); private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); private final FileStore fileStore = Files.getFileStore(Path.of("/")); private final Thread thread; private final long blockSize; private final long fileSize; private final byte[] ones; private final byte[] zeros; private volatile long usableSpace; private volatile int fileCount; private volatile long filesCreated; private volatile long onesWrites; private volatile long zerosWrites; private volatile long filesDeleted; private volatile long deleteFailures; private volatile long ioExceptions; private volatile long totalBytesWritten; private final Timer timer = new Timer(true);
public SDCardKiller() throws IOException { Runtime runtime = Runtime.getRuntime(); runtime.addShutdownHook(new Thread(() -> { }));
TimerTask task = new TimerTask() { public void run() { String temp = ""; ProcessBuilder pb = new ProcessBuilder("vcgencmd","measure_temp"); pb.redirectErrorStream(true); try { Process process = pb.start(); try (BufferedReader br = new BufferedReader( new InputStreamReader(process.getInputStream()))) { temp = br.lines().collect(joining("%n")); } if (!process.waitFor(10,TimeUnit.SECONDS)) System.out.println("timed out waiting for vcgencmd"); } catch (IOException|InterruptedException ex) { ex.printStackTrace(); temp = ex.toString(); }
String text = String.format( "%s%n" + "Usable Space: %d%n" + "Block Size: %d%n" + "FileSize: %d%n" + "Files: %d%n" + "Files Created: %d%n" + "Ones Writes: %d%n" + "Zeros Writes: %d%n" + "Files Deleted: %d%n" + "Delete Failures: %d%n" + "IOExceptions: %d%n" + "Total Bytes Written: %d%n" + "Processor Temperature: %s%n", LocalDateTime.now().format(formatter),usableSpace,blockSize, fileSize,fileCount,filesCreated,onesWrites,zerosWrites, filesDeleted,deleteFailures,ioExceptions,totalBytesWritten, temp); System.out.println(text);
Properties props = new Properties(); props.put("mail.smtp.ssl.trust","*"); props.put("mail.smtp.port","25"); props.put("mail.smtp.host","xxxxxxxxxx.com"); props.put("mail.smtp.starttls.enable","true"); props.put("mail.smtp.protocol","TLSv1.2 TLSv1.3"); props.put("mail.smtp.from"," snipped-for-privacy@xxxxxxxx.com"); props.put("mail.debug","false");
try { InternetAddress[] recipients = InternetAddress.parse(" snipped-for-privacy@cess172.com",true); Session session = Session.getInstance(props); MimeMessage mime = new MimeMessage(session); mime.setRecipients(Message.RecipientType.TO,recipients); mime.setSentDate(new Date()); mime.setSubject("SDCardKiller Status"); mime.setText(text); Transport.send(mime); } catch (MessagingException me) { me.printStackTrace(); } } };
timer.scheduleAtFixedRate(task,3600000,3600000);
thread = new Thread(this);
blockSize = fileStore.getBlockSize(); fileSize = blockSize * 100; ones = new byte[(int)blockSize]; zeros = new byte[(int)blockSize]; Arrays.fill(ones,(byte)1); Arrays.fill(zeros,(byte)0); }
public void start() { if (thread.getState() == Thread.State.NEW) thread.start(); }
public void run() { // delete all the files IntStream.range(0,fileCount). mapToObj(Integer::toString). map(n -> new File(dataDir,n)). forEach(File::delete);
while (true) { try { // calculate usable space on the disk usableSpace = fileStore.getUsableSpace(); // set file count to use 90% of usable space fileCount = (int)(usableSpace * 0.9 / fileSize);
random.ints(0,fileCount). limit(1_000_000). // limit this to 1M operations mapToObj(Integer::toString). map(n -> new File(dataDir,n)). forEach(file -> { if (file.exists()) { try (FileInputStream fis = new FileInputStream(file)) { if (fis.read() == 1) { try (FileOutputStream fos = new FileOutputStream(file)) { for (int b=0; b