summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKen Sumrall <ksumrall@android.com>2012-05-30 16:44:40 -0700
committerKen Sumrall <ksumrall@android.com>2012-06-14 17:13:54 -0700
commit2633ce5ca5024d5565c9068c29ec39a8c3ed10e9 (patch)
treebe6087153d48f1781461b3d4587c02d5ebfd57ba
parentb9314021ee1ccaa62eb115e8e0188f482a950f3b (diff)
downloadextras-2633ce5ca5024d5565c9068c29ec39a8c3ed10e9.tar.gz
Add some tools for testing ext4 performance and resiliency.
rand_emmc_perf is a simple test to test random read/write performance of emmc chips. android_emmc_perf_tests is a script that runs on the host that uses rand_emmc_perf to test the emmc performance on a device. corrupt_gdt_free_blocks is used to corrupt a filesystem so the kernel trips over it at runtime and panics, thus testing kernel's ability to mark the filesystem as needing to be fixed. set_ext4_err_bit sets the error bit in the superblock so e2fsck will trigger a full check next boot. corrupt_gdt_free_blocks and set_ext4_err_bit are only built for userdebug and eng builds. rand_emmc_perf is marked optional, and not included in any build by default. Change-Id: I808174025d891f358ac54008371cb590e3c19f2f
-rw-r--r--tests/ext4/Android.mk35
-rwxr-xr-xtests/ext4/android_emmc_perf_tests.sh122
-rw-r--r--tests/ext4/corrupt_gdt_free_blocks.c100
-rw-r--r--tests/ext4/rand_emmc_perf.c129
-rw-r--r--tests/ext4/set_ext4_err_bit.c62
5 files changed, 448 insertions, 0 deletions
diff --git a/tests/ext4/Android.mk b/tests/ext4/Android.mk
new file mode 100644
index 00000000..36f2e17d
--- /dev/null
+++ b/tests/ext4/Android.mk
@@ -0,0 +1,35 @@
+# Copyright 2012 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= corrupt_gdt_free_blocks.c
+
+LOCAL_MODULE:= corrupt_gdt_free_blocks
+LOCAL_MODULE_TAGS := debug
+LOCAL_C_INCLUDES += system/extras/ext4_utils
+
+include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= set_ext4_err_bit.c
+
+LOCAL_MODULE:= set_ext4_err_bit
+LOCAL_MODULE_TAGS := debug
+
+include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= rand_emmc_perf.c
+
+LOCAL_MODULE:= rand_emmc_perf
+LOCAL_MODULE_TAGS := optional
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_STATIC_LIBRARIES := libc
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/tests/ext4/android_emmc_perf_tests.sh b/tests/ext4/android_emmc_perf_tests.sh
new file mode 100755
index 00000000..23190f8c
--- /dev/null
+++ b/tests/ext4/android_emmc_perf_tests.sh
@@ -0,0 +1,122 @@
+#!/bin/bash
+
+PERF="rand_emmc_perf"
+
+if [ ! -r "$PERF" ]
+then
+ echo "Cannot read $PERF test binary"
+fi
+
+if [ ! -r "$PERF_OSYNC" ]
+then
+ echo "Cannot read $PERF_OSYNC test binary"
+fi
+
+if ! adb shell true >/dev/null 2>&1
+then
+ echo "No device detected over adb"
+fi
+
+HARDWARE=`adb shell getprop ro.hardware | tr -d "\r"`
+
+case "$HARDWARE" in
+ tuna | steelhead)
+ CPUFREQ="/sys/devices/system/cpu/cpu0/cpufreq"
+ CACHE="/dev/block/platform/omap/omap_hsmmc.0/by-name/cache"
+ ;;
+
+ stingray | wingray)
+ CPUFREQ="/sys/devices/system/cpu/cpu0/cpufreq"
+ CACHE="/dev/block/platform/sdhci-tegra.3/by-name/cache"
+ ;;
+
+ herring)
+ echo "This test will wipe the userdata partition on $HARDWARE devices."
+ read -p "Do you want to proceed? " ANSWER
+
+ if [ "$ANSWER" != "yes" ]
+ then
+ echo "aborting test"
+ exit 1
+ fi
+
+ CPUFREQ="/sys/devices/system/cpu/cpu0/cpufreq"
+ CACHE="/dev/block/platform/s3c-sdhci.0/by-name/userdata"
+ ;;
+
+ grouper)
+ CPUFREQ="/sys/devices/system/cpu/cpu0/cpufreq"
+ CACHE="/dev/block/platform/sdhci-tegra.3/by-name/CAC"
+ ;;
+
+ *)
+ echo "Unknown hardware $HARDWARE. Exiting."
+ exit 1
+esac
+
+# prepare the device
+adb root
+adb wait-for-device
+adb push "$PERF" /dev
+adb push "$PERF_OSYNC" /dev
+adb shell stop
+adb shell stop sdcard
+adb shell stop ril-daemon
+adb shell stop media
+adb shell stop drm
+adb shell stop keystore
+adb shell stop tf_daemon
+adb shell stop bluetoothd
+adb shell stop hciattach
+adb shell umount /sdcard >/dev/null 2>&1
+adb shell umount /data >/dev/null 2>&1
+adb shell umount /cache >/dev/null 2>&1
+# Add more services here that other devices need to stop.
+# So far, this list is sufficient for:
+# Prime
+
+# At this point, the device is quiescent, need to crank up the cpu speed,
+# then run tests
+adb shell "cat $CPUFREQ/cpuinfo_max_freq > $CPUFREQ/scaling_max_freq"
+adb shell "cat $CPUFREQ/cpuinfo_max_freq > $CPUFREQ/scaling_min_freq"
+
+# Start the tests
+
+# Sequential read test
+for I in 1 2 3
+do
+ echo "Sequential read test $I"
+ adb shell dd if="$CACHE" of=/dev/null bs=1048576 count=200
+done
+
+# Sequential write test
+for I in 1 2 3
+do
+ echo "Sequential write test $I"
+ adb shell dd if=/dev/zero of="$CACHE" bs=1048576 count=200
+done
+
+# Random read test
+for I in 1 2 3
+do
+ echo "Random read test $I"
+ adb shell /dev/"$PERF" -r 100 "$CACHE"
+done
+
+# Random write test
+for I in 1 2 3
+do
+ echo "Random write test $I"
+ adb shell /dev/"$PERF" -w 100 "$CACHE"
+done
+
+# Random write test with O_SYNC
+for I in 1 2 3
+do
+ echo "Random write with o_sync test $I"
+ adb shell /dev/"$PERF" -w 100 -o "$CACHE"
+done
+
+# Make a new empty /cache filesystem
+adb shell make_ext4fs "$CACHE"
+
diff --git a/tests/ext4/corrupt_gdt_free_blocks.c b/tests/ext4/corrupt_gdt_free_blocks.c
new file mode 100644
index 00000000..7be737da
--- /dev/null
+++ b/tests/ext4/corrupt_gdt_free_blocks.c
@@ -0,0 +1,100 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ext4.h"
+#include "ext4_utils.h"
+
+#define SB_OFFSET 1024
+
+int main(int argc, char *argv[])
+{
+ char me[] = "corrupt_gdt_free_blocks";
+ int fd;
+ int block_size;
+ int num_bgs;
+ int i;
+ struct ext4_super_block sb;
+ struct ext2_group_desc gd;
+
+ if (argc != 2) {
+ fprintf(stderr, "%s: Usage: %s <ext4_block_device>\n", me, me);
+ exit(1);
+ }
+
+ fd = open(argv[1], O_RDWR);
+
+ if (fd < 0) {
+ fprintf(stderr, "%s: Cannot open block device %s\n", me, argv[1]);
+ exit(1);
+ }
+
+ if (lseek(fd, SB_OFFSET, SEEK_SET) == -1) {
+ fprintf(stderr, "%s: Cannot lseek to superblock to read\n", me);
+ exit(1);
+ }
+
+ if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) {
+ fprintf(stderr, "%s: Cannot read superblock\n", me);
+ exit(1);
+ }
+
+ if (sb.s_magic != 0xEF53) {
+ fprintf(stderr, "%s: invalid superblock magic\n", me);
+ exit(1);
+ }
+
+ /* Make sure the block size is 2K or 4K */
+ if ((sb.s_log_block_size != 1) && (sb.s_log_block_size != 2)) {
+ fprintf(stderr, "%s: block size not 2K or 4K\n", me);
+ exit(1);
+ }
+
+ block_size = 1 << (10 + sb.s_log_block_size);
+ num_bgs = DIV_ROUND_UP(sb.s_blocks_count_lo, sb.s_blocks_per_group);
+
+ if (sb.s_desc_size != sizeof(struct ext2_group_desc)) {
+ fprintf(stderr, "%s: Can't handle block group descriptor size of %d\n",
+ me, sb.s_desc_size);
+ exit(1);
+ }
+
+ /* read first block group descriptor, decrement free block count, and
+ * write it back out
+ */
+ if (lseek(fd, block_size, SEEK_SET) == -1) {
+ fprintf(stderr, "%s: Cannot lseek to block group descriptor table to read\n", me);
+ exit(1);
+ }
+
+ /* Read in block group descriptors till we read one that has at least one free block */
+
+ for (i=0; i < num_bgs; i++) {
+ if (read(fd, &gd, sizeof(gd)) != sizeof(gd)) {
+ fprintf(stderr, "%s: Cannot read group descriptor %d\n", me, i);
+ exit(1);
+ }
+ if (gd.bg_free_blocks_count) {
+ break;
+ }
+ }
+
+ gd.bg_free_blocks_count--;
+
+ if (lseek(fd, -sizeof(gd), SEEK_CUR) == -1) {
+ fprintf(stderr, "%s: Cannot lseek to block group descriptor table to write\n", me);
+ exit(1);
+ }
+
+ if (write(fd, &gd, sizeof(gd)) != sizeof(gd)) {
+ fprintf(stderr, "%s: Cannot write modified group descriptor\n", me);
+ exit(1);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
diff --git a/tests/ext4/rand_emmc_perf.c b/tests/ext4/rand_emmc_perf.c
new file mode 100644
index 00000000..d55adc93
--- /dev/null
+++ b/tests/ext4/rand_emmc_perf.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* A simple test of emmc random read and write performance. When testing write
+ * performance, try it twice, once with O_SYNC compiled in, and once with it commented
+ * out. Without O_SYNC, the close(2) blocks until all the dirty buffers are written
+ * out, but the numbers tend to be higher.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+#define TST_BLK_SIZE 4096
+/* Number of seconds to run the test */
+#define TEST_LEN 10
+
+static void usage(void) {
+ fprintf(stderr, "Usage: rand_emmc_perf [ -r | -w ] [-o] <size_in_mb> <block_dev>\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ long max_blocks;
+ int fd, fd2, write_mode = 0, iops = 0;
+ struct timeval start, end, res;
+ unsigned int seed;
+ char buf[TST_BLK_SIZE] = { 0 };
+ int c;
+ int o_sync = 0;
+
+ while ((c = getopt(argc, argv, "+rwo")) != -1) {
+ switch (c) {
+ case '?':
+ default:
+ usage();
+ break;
+
+ case 'r':
+ /* Do nothing, read mode is the default */
+ break;
+
+ case 'w':
+ write_mode = 1;
+ break;
+
+ case 'o':
+ o_sync = O_SYNC;
+ break;
+ }
+ }
+
+ if (o_sync && !write_mode) {
+ /* Can only specify o_sync in write mode. Probably doesn't matter,
+ * but clear o_sync if in read mode */
+ o_sync = 0;
+ }
+
+ if ((argc - optind) != 2) {
+ usage();
+ }
+
+ /* Size is given in megabytes, so compute the number of TST_BLK_SIZE blocks. */
+ max_blocks = atol(argv[2]) * ((1024*1024) / TST_BLK_SIZE);
+
+ if ((fd = open(argv[3], O_RDWR | o_sync)) < 0) {
+ fprintf(stderr, "Cannot open block device %s\n", argv[2]);
+ exit(1);
+ }
+
+ fd2 = open("/dev/urandom\n", O_RDONLY);
+ if (fd2 < 0) {
+ fprintf(stderr, "Cannot open /dev/urandom\n");
+ }
+ if (read(fd2, &seed, sizeof(seed)) != sizeof(seed)) {
+ fprintf(stderr, "Cannot read /dev/urandom\n");
+ }
+ close(fd2);
+ srand(seed);
+
+ res.tv_sec = 0;
+ gettimeofday(&start, 0);
+ while (res.tv_sec < TEST_LEN) {
+ if (lseek64(fd, (rand() % max_blocks) * TST_BLK_SIZE, SEEK_SET) < 0) {
+ fprintf(stderr, "lseek64 failed\n");
+ }
+ if (write_mode) {
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ fprintf(stderr, "Short write\n");
+ }
+ } else {
+ if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ fprintf(stderr, "Short read\n");
+ }
+ }
+ iops++;
+ gettimeofday(&end, 0);
+ timersub(&end, &start, &res);
+ }
+ close(fd);
+
+ /* The close can take a while when in write_mode as buffers are flushed.
+ * So get the time again. */
+ gettimeofday(&end, 0);
+ timersub(&end, &start, &res);
+
+ printf("%d iops/sec\n", iops / (int) res.tv_sec);
+
+ exit(0);
+}
+
diff --git a/tests/ext4/set_ext4_err_bit.c b/tests/ext4/set_ext4_err_bit.c
new file mode 100644
index 00000000..88893d84
--- /dev/null
+++ b/tests/ext4/set_ext4_err_bit.c
@@ -0,0 +1,62 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define SB_OFFSET 1024
+#define SB_SIZE 1024
+#define EXT4_MAGIC_OFFSET 0x38
+#define EXT4_STATE_OFFSET 0x3A
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ char me[] = "set_ext4_err_bit";
+ unsigned char sb[1024];
+
+ if (argc != 2) {
+ fprintf(stderr, "%s: Usage: %s <ext4_block_device>\n", me, me);
+ exit(1);
+ }
+
+ fd = open(argv[1], O_RDWR);
+
+ if (fd < 0) {
+ fprintf(stderr, "%s: Cannot open block device %s\n", me, argv[1]);
+ exit(1);
+ }
+
+ if (lseek(fd, SB_OFFSET, SEEK_SET) == -1) {
+ fprintf(stderr, "%s: Cannot lseek to superblock to read\n", me);
+ exit(1);
+ }
+
+ if (read(fd, sb, SB_SIZE) != SB_SIZE) {
+ fprintf(stderr, "%s: Cannot read superblock\n", me);
+ exit(1);
+ }
+
+ if ((sb[EXT4_MAGIC_OFFSET] != 0x53) || (sb[EXT4_MAGIC_OFFSET+1] != 0xEF)) {
+ fprintf(stderr, "%s: invalid superblock magic\n", me);
+ exit(1);
+ }
+
+ /* Set the errors detected bit */
+ sb[EXT4_STATE_OFFSET] |= 0x2;
+
+ if (lseek(fd, SB_OFFSET, SEEK_SET) == -1) {
+ fprintf(stderr, "%s: Cannot lseek to superblock to write\n", me);
+ exit(1);
+ }
+
+ if (write(fd, sb, SB_SIZE) != SB_SIZE) {
+ fprintf(stderr, "%s: Cannot write superblock\n", me);
+ exit(1);
+ }
+
+ close(fd);
+
+ return 0;
+}
+