/* * Copyright (C) 2007 Nokia Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Author: Adrian Hunter */ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdint.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <sys/time.h> #include <time.h> #include <limits.h> #include "tests.h" #define BLOCK_SIZE 32 * 1024 struct timeval tv_start; struct timeval tv_stop; static inline void start_timer(void) { CHECK(gettimeofday(&tv_start, NULL) != -1); } static inline long long stop_timer(void) { long long usecs; CHECK(gettimeofday(&tv_stop, NULL) != -1); usecs = (tv_stop.tv_sec - tv_start.tv_sec); usecs *= 1000000; usecs += tv_stop.tv_usec; usecs -= tv_start.tv_usec; return usecs; } static unsigned speed(size_t bytes, long long usecs) { unsigned long long k; k = bytes * 1000000ULL; k /= usecs; k /= 1024; CHECK(k <= UINT_MAX); return (unsigned) k; } void perf(void) { pid_t pid; int fd, i; ssize_t written, readsz; size_t remains, block, actual_size; char file_name[256]; unsigned char *buf; long long write_time, unmount_time, mount_time, read_time; /* Sync all file systems */ sync(); /* Make random data to write */ buf = malloc(BLOCK_SIZE); CHECK(buf != NULL); pid = getpid(); srand(pid); for (i = 0; i < BLOCK_SIZE; i++) buf[i] = rand(); /* Open file */ tests_cat_pid(file_name, "perf_test_file_", pid); start_timer(); fd = open(file_name, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); CHECK(fd != -1); CHECK(tests_size_parameter > 0); CHECK(tests_size_parameter <= SIZE_MAX); /* Write to file */ actual_size = 0; remains = tests_size_parameter; while (remains > 0) { if (remains > BLOCK_SIZE) block = BLOCK_SIZE; else block = remains; written = write(fd, buf, block); if (written <= 0) { CHECK(errno == ENOSPC); /* File system full */ errno = 0; break; } remains -= written; actual_size += written; } CHECK(fsync(fd) != -1); CHECK(close(fd) != -1); write_time = stop_timer(); /* Unmount */ start_timer(); tests_unmount(); unmount_time = stop_timer(); /* Mount */ start_timer(); tests_mount(); mount_time = stop_timer(); /* Open file, read it, and close it */ start_timer(); fd = open(file_name, O_RDONLY); CHECK(fd != -1); remains = actual_size; while (remains > 0) { if (remains > BLOCK_SIZE) block = BLOCK_SIZE; else block = remains; readsz = read(fd, buf, block); CHECK(readsz == block); remains -= readsz; } CHECK(close(fd) != -1); read_time = stop_timer(); CHECK(unlink(file_name) != -1); /* Display timings */ printf("File system read and write speed\n"); printf("================================\n"); printf("Specfied file size: %lld\n", tests_size_parameter); printf("Actual file size: %zu\n", actual_size); printf("Write time (us): %lld\n", write_time); printf("Unmount time (us): %lld\n", unmount_time); printf("Mount time (us): %lld\n", mount_time); printf("Read time (us): %lld\n", read_time); printf("Write speed (KiB/s): %u\n", speed(actual_size, write_time)); printf("Read speed (KiB/s): %u\n", speed(actual_size, read_time)); printf("Test completed\n"); } /* Title of this test */ const char *perf_get_title(void) { return "Measure file system read and write speed"; } /* Description of this test */ const char *perf_get_description(void) { return "Syncs the file system (a newly created empty file system is " \ "preferable). Creates a file named perf_test_file_pid, where " \ "pid is the process id. The file is filled with random data. " \ "The size of the file is given by the -z or --size option, " \ "otherwise it defaults to 10MiB. Unmounts the file system. " \ "Mounts the file system. Reads the entire file in 32KiB size " \ "blocks. Displays the time taken for each activity. Deletes " \ "the file. Note that the file is synced after writing and " \ "that time is included in the write time and speed."; } int main(int argc, char *argv[]) { int run_test; /* Set default test file size */ tests_size_parameter = 10 * 1024 * 1024; /* Handle common arguments */ run_test = tests_get_args(argc, argv, perf_get_title(), perf_get_description(), "z"); if (!run_test) return 1; /* Change directory to the file system and check it is ok for testing */ tests_check_test_file_system(); /* Do the actual test */ perf(); return 0; }