Linux, disk subsystem crash test
От: screw_cms Россия ICQ: 168185721
Дата: 31.03.04 12:00
Оценка: 4 (1)
Это вполне рабочая утилита, нагружающая дисковую и файловую подсистему "на отказ". Писалась и использовалась для тестирования рабочего сервера. Здесь размещаю just for fun.

/*********************************************************

           FILE: crashtest.c
    DESCRIPTION: multithreaded crashtest for disk-subsustem
         AUTHOR: Artamonov Dmitry, screw_cms@mail.ru
        HISTORY: 08-09/07/2003, Screw: initial version (stable)
             11/07/2003, Wizard: parsing command line by getopt
             16/07/2003, Screw: optional make (argv/getopt arguments)
             

        COMMENT: It's my first trip in Linux programming, but i'm prefer Win32 platform
         THANKS: Evseev Alexander (Wizard) for help in foreign OS
           
           MAKE: g++ -L/usr/lib -lpthread -o disktest disktest.cpp

!!! FIX_ME: open_dir is NO REEINTRANT function!!!

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version
    2 of the License, or (at your option) any later version.

*********************************************************/

#define O_GETOPT_PARSING // comment it for argv[] parsing case

// basically console IO
#include <stdio.h>
#include <stdlib.h>

// strings
#include <string.h>
// directory scanning
#include "dirent.h"

// for directory STATus
#include "sys/stat.h"
#include "sys/types.h"
#include "unistd.h"

// threads
#include "pthread.h"

#define MAX_PATH_LENGTH 32768

// variety selecting directory in current hierarchy, in percent(%)
#define SEL_DIR_FREQ 30

#define C_COMMANDS_COUNT 6
#define C_MAKE_DIR        0
#define C_REM_DIR        1
#define C_MAKE_FILE        2
#define C_REM_FILE        3
#define C_RAND_READ        4
#define C_RAND_WRITE    5

// file sizes in Kb
#define C_FILE_MIN_SIZE 200
#define C_FILE_MAX_SIZE 20000
#define C_THREAD_DEF    20
#define C_TIME_DEF    1

int file_min_size;
int file_max_size;
#define C_FILE_BUFFER_SIZE 65536

int my_rand()
{
    static unsigned int numb = 0;
    if (!numb) numb = rand();
    return numb++;
}

//===========================================================================================
// Selecting random directory into 'root_dir' directories tree.
// if selecting is done routine returns TRUE and copies founded path into root_dir string
//===========================================================================================
bool select_directory(char* root_dir)
{
    char path[MAX_PATH_LENGTH];
    int dir_count, dir_num, level = 0;

    DIR *dp;
    struct dirent *ep;

    if (!strcmp( root_dir, "/dev") || !strcmp( root_dir, "/proc")) return false;

    for (;;)
    {
        dp = opendir(root_dir);
        if (!dp) return false;// open dir fails

        dir_count = 0;

        while ( ep = readdir (dp) )
        {
            if (strcmp(ep->d_name,"..") && strcmp(ep->d_name,".") )
            {
                strcpy( path, root_dir );
                if (strlen( path) > 1)
                    strcat( path, "/");
                strcat( path, ep->d_name );

                struct stat stat_buff;
                stat( path, &stat_buff );
                if ( S_ISDIR( stat_buff.st_mode ) /*&& !S_ISLNK( stat_buff.st_mode )*/ )
                    dir_count++;
            }
        }
        closedir( dp );
        if (0==dir_count) break;
        // select random directory
        dir_num = my_rand()%dir_count;

        dp = opendir(root_dir);
        if (!dp) return false;// open dir fails
        dir_count = 0;
        while ( ep = readdir (dp) )
        {
            if (strcmp(ep->d_name,"..") && strcmp(ep->d_name,".") )
            {
                strcpy( path, root_dir );
                if (strlen( path) > 1)
                    strcat( path, "/");
                strcat( path, ep->d_name );

                struct stat stat_buff;
                stat( path, &stat_buff );
                if ( S_ISDIR( stat_buff.st_mode ) /*&& !S_ISLNK( stat_buff.st_mode )*/ )
                    if ( dir_num == dir_count)
                        break;
                    else
                        dir_count++;
            } 
        }
        closedir( dp );

        // check that directory was founded
        if (dir_count != dir_num)
        {
            dir_count = 0;
            break;
        }
        strcpy( root_dir, path ); // returning current dir
        if (my_rand()%100 < SEL_DIR_FREQ)
            return true;

        level++;
    }

    // return's current directory if search fails
    if (0 == dir_count)
    {
        // no directories we're founded, but level is childest - then return TRUE!
        return (level>0);
    }

}

//===========================================================================================
// Selecting random file in 'root_dir' directory
// Sipmle analog with 'select_directory' routine
//===========================================================================================
bool select_file(char *root_dir)
{
    char path[MAX_PATH_LENGTH];
    int file_count, file_num, level = 0;

    DIR *dp;
    struct dirent *ep;

    if (!strcmp( root_dir, "/dev") || !strcmp( root_dir, "/proc")) return false;

    dp = opendir(root_dir);
    if (!dp) return false;// open dir fails

    file_count = 0;        
    while ( ep = readdir (dp) )
    {
        if (strcmp(ep->d_name,"..") && strcmp(ep->d_name,".") )
        {
            strcpy( path, root_dir );
            if (strlen( path) > 1)
                strcat( path, "/");
            strcat( path, ep->d_name );

            struct stat stat_buff;
            stat( path, &stat_buff );
            if ( !S_ISDIR( stat_buff.st_mode ) /*&& !S_ISLNK( stat_buff.st_mode )*/ )
                file_count++;
        }
    }
    closedir( dp );
    if (0==file_count) return false;

    // select random file 
    file_num = my_rand()%file_count;

    dp = opendir(root_dir);
    if (!dp) return false;// open dir fails

    file_count = 0;        
    while ( ep = readdir (dp) )
    {
        if (strcmp(ep->d_name,"..") && strcmp(ep->d_name,".") )
        {
            strcpy( path, root_dir );
            if (strlen( path) > 1)
                strcat( path, "/");
            strcat( path, ep->d_name );

            struct stat stat_buff;
            stat( path, &stat_buff );
            if ( !S_ISDIR( stat_buff.st_mode ) /*&& !S_ISLNK( stat_buff.st_mode )*/ )
                if ( file_num == file_count)
                    break;
                else
                    file_count++;
        }
    }
    closedir( dp );
    // check that file was founded
    if (file_count != file_num)
        return false;

    strcpy( root_dir, path ); // returning current file
    return true;
}

//===========================================================================================
// Deleting sub-tree recursivelly with all content
// returns deleted files count
// 'dirs' parameter is increased by deleted folders count
//===========================================================================================
int del_tree(char* root_dir, int &dirs)
{
    char path[MAX_PATH_LENGTH];    

    DIR *dp;
    struct dirent *ep;
    int count = 0;

    if (!strcmp( root_dir, "/dev") || !strcmp( root_dir, "/proc")) 
        return 0;

    dp = opendir (root_dir);
    if (dp != NULL)
    {
        while ( ep = readdir (dp) )
        {
            if (strcmp(ep->d_name,"..") && strcmp(ep->d_name,".") )
            {
                strcpy( path, root_dir );
                if (strlen( path) > 1)
                    strcat( path, "/");
                strcat( path, ep->d_name );

                struct stat stat_buff;
                stat( path, &stat_buff );
                if ( S_ISDIR( stat_buff.st_mode ) /*&& !S_ISLNK( stat_buff.st_mode )*/ )
                {
                    count += del_tree( path, dirs );
                    dirs++;
                }    
                else
                    count++;
                remove( path );
            }
        }
        if (!remove( root_dir ))
            dirs++;
    }
    return count;
}

//===========================================================================================
// POSIX thread block with program-logic implementation
//===========================================================================================

#define PTH_INIT 0
#define PTH_RUNNING 1
#define PTH_STOP_PENDING 2
#define PTH_EXITED 3

// work statistic
typedef struct {
    unsigned int f_created;
    unsigned int f_erased;
    unsigned int d_created;
    unsigned int d_erased;
    unsigned int reading;
    unsigned int writing;    
    bool is_FS_full;
} statistic, *statistic_ptr;

// thread arguments
typedef struct {
    int th_num;
    pthread_t THREAD;
    int state;
    char *root_dir;
    void *buffer;
    statistic_ptr p_st;
} thread_arg, *thread_arg_ptr;

// MAIN thread working routine
void* thread_func( void* arg )
{    
    void *retval;
    thread_arg_ptr p_arg = (thread_arg_ptr)arg;
    char path[MAX_PATH_LENGTH];
    char name[MAX_PATH_LENGTH];
    int cmd, rnd, dirs, res;
    unsigned int size, bl_pos, bl_size;

    //    printf("Thread %i started!\n", p_arg->th_num);
    p_arg->state = PTH_RUNNING;


    while ( PTH_STOP_PENDING != p_arg->state )
    {
        strcpy( path, p_arg->root_dir );

        if (!select_directory( path ))
            strcpy( path, p_arg->root_dir ); // selection failure, work with root dir

        // select command
        rnd = rand()%100;

        if (p_arg->p_st->is_FS_full)
        {
            // filesystem is full, enable removing content
            if ( rnd < 50 ) cmd = C_MAKE_FILE;
            else
                if (rnd < 70) cmd = C_MAKE_DIR;
                else
                    if (rnd < 80) cmd = C_REM_FILE;
                    else
                        if (rnd < 85)
                        {
                        // lock removing root directory
                            if (strcmp( path, p_arg->root_dir))
                                cmd = C_REM_DIR;
                            else
                                cmd = 0;
                        }
                        else
                            if (rnd<93) cmd = C_RAND_READ;
                            else cmd = C_RAND_WRITE;
        } else {
            // filesystem while is empty - filling one
            if ( rnd < 65 ) cmd = C_MAKE_FILE;
            else
                if (rnd < 85) cmd = C_MAKE_DIR;
                else
                    if (rnd<93) cmd = C_RAND_READ;
                    else cmd = C_RAND_WRITE;            
        }

        // executing command
        switch( cmd )
        {
            case C_MAKE_DIR:    {
                    if (rand()%100 < SEL_DIR_FREQ)
                        // with SEL_DIR_FREQUENCY we're creating directory in root_dir folder
                        strcpy( path, p_arg->root_dir );
                    sprintf( name, "%s/%i.%i", path, my_rand(), p_arg->th_num );
                    if ( !mkdir( name, 493 ) )
                        p_arg->p_st->d_created++;
                    break;
                }

            case C_REM_DIR:        {
                    dirs = 0;
                    p_arg->p_st->f_erased += del_tree( path, dirs );
                    p_arg->p_st->d_erased += dirs;
                    break;
                }
            case C_MAKE_FILE:    {
                    sprintf( name, "%s/%i.%i", path, my_rand(), p_arg->th_num );
                    FILE* f = fopen(name, "wb");

                    if (f)
                    {
                        size = file_min_size + rand()%(file_max_size - file_min_size);
                        size*=1024;
                        while (size>0)
                        {
                            if (size >= C_FILE_BUFFER_SIZE)
                            {
                                res = fwrite( p_arg->buffer, 1, C_FILE_BUFFER_SIZE, f );
                                if (res != C_FILE_BUFFER_SIZE)
                                    p_arg->p_st->is_FS_full = true;
                                size-=C_FILE_BUFFER_SIZE;
                            } else {
                                res = fwrite( p_arg->buffer, 1, size, f );
                                if (res != size)
                                    p_arg->p_st->is_FS_full = true;
                                size=0;
                            }
                        }
                        fclose( f );
                        p_arg->p_st->f_created++;
                        break;
                    }
                }
            case C_REM_FILE:    {
                    if (select_file(path))
                    {            
                        if (!remove(path))
                            p_arg->p_st->f_erased++;
                    }                        
                    break;
                }
            case C_RAND_READ:    
            case C_RAND_WRITE:    {

                    if (select_file(path))
                    {                            
                        FILE* f;
                        if (C_RAND_READ == cmd)
                            f = fopen( path, "rb" );
                        else
                            f = fopen( path, "wb" );
                        if (f)
                        {
                            struct stat stat_buff;
                            stat( path, &stat_buff );
                            size = stat_buff.st_size;
                            if (size>0)
                            {
                            // file size is enough
                                bl_pos = rand()%size;
                                bl_size = rand()%(size-bl_pos+1)+1;

                                while (bl_size>0)
                                {
                                    if ( bl_size >= C_FILE_BUFFER_SIZE )
                                        size = C_FILE_BUFFER_SIZE;
                                    else
                                        size = bl_size;

                                    if (!fseek(f, bl_pos, SEEK_SET) )
                                    {
                                        if ( C_RAND_READ == cmd )
                                            fread(p_arg->buffer, 1, size, f);
                                        else
                                            fwrite(p_arg->buffer, 1, size, f);                                        
                                    }
                                    bl_size -= size;
                                }
                            }
                            fclose(f);

                            if (C_RAND_READ == cmd)
                                p_arg->p_st->reading++;
                            else
                                p_arg->p_st->writing++;
                        }
                    }
                    break;
                }
        }

    }
    p_arg->state = PTH_EXITED;
    pthread_exit( retval );
}

void usage(const char * pr_name) {

#ifdef O_GETOPT_PARSING
  printf("Disk subsustem crash-test. Was written by (c)2003 Screw\n" \
    "USAGE:\n%s dirpath [-c count] [-t mins] [-m min_size] [-M max_size]\n" \
    "Where: dirpath - working directory/device\n\n" \
    "       c       - threads counts (%i by default)\n" \
    "       t       - time of test execution (in min, %i by default)\n" \
    "       m       - minimal work-files size (in Kb, %i by default)\n" \
    "       M       - maximal work-files size (in Kb, %i by default)\n\n", pr_name, C_THREAD_DEF, C_TIME_DEF, C_FILE_MIN_SIZE, C_FILE_MAX_SIZE \
  );
#else
  printf("Disk subsustem crash-test. Was written by (c)2003 Screw\n" \
    "USAGE:\n%s dirpath [-c] [-t] [-m] [-M]\n" \
    "Where: dirpath - working directory/device\n\n" \
    "       c       - threads counts (%i by default)\n" \
    "       t       - time of test execution (in min, %i by default)\n" \
    "       m       - minimal work-files size (in Kb, %i by default)\n" \
    "       M       - maximal work-files size (in Kb, %i by default)\n\n", pr_name, C_THREAD_DEF, C_TIME_DEF, C_FILE_MIN_SIZE, C_FILE_MAX_SIZE \
  );
#endif
  return;
}

//===========================================================================================
// MAIN PROGRAMM
//===========================================================================================
int main( int argc, char *argv[]) 
{ 
    if (argc < 2)
    {
      usage(argv[0]);
      exit(1);
    }


    char root_dir[MAX_PATH_LENGTH];    
    int th_count = C_THREAD_DEF;
    int testing_time = C_TIME_DEF; // in min
    file_min_size = C_FILE_MIN_SIZE;
    file_max_size = C_FILE_MAX_SIZE;
    time_t start, finish;
    int i;

#ifdef O_GETOPT_PARSING
    // initialization - parsing command-line arguments
    char ch;
    char *optstr = "c:t:m:M:";
    while( -1 != (ch=getopt(argc,argv,optstr))) {
      switch(ch) {
        case 'c':
          th_count = atoi( optarg );
          break;
        case 't':
          testing_time = atoi( optarg );
          break;
        case 'm':
          file_min_size = atoi( optarg );
          break;
        case 'M':
          file_max_size = atoi( optarg );
          break;
        default:
          usage(argv[0]);
          exit (1);
      }
    }
    if ( optind+1 > argc ) {
      usage(argv[0]);
      exit(1);
    } else {
      strcpy( root_dir, argv[optind] );
    }

#else

    strcpy( root_dir, argv[1] );
    if (argc>2)
    {
        th_count = atoi( argv[2] );
        if (th_count<=0)
        {
            printf("Invalid 'th' parameter-value specified! Must be > 0!\n");
            return 0;
        }
    }
    if (argc>3)
    {
        testing_time = atoi( argv[3] );
        if (testing_time<=0)
        {
            printf("Invalid 't' parameter-value specified! Must be > 0!\n");
            return 0;
        }        
    }
    if (argc>4)
    {
        file_min_size = atoi( argv[4] );
        if (testing_time<0)
        {
            printf("Invalid 'minsize' parameter-value specified! Must be >= 0!\n");
            return 0;
        }        
    }
    if (argc>5)
    {
        file_max_size = atoi( argv[5] );
        if (file_max_size<file_min_size)
        {
            printf("Invalid 'maxsize' parameter-value specified! Must be >= %i!", file_min_size);
            return 0;
        }        
    }
#endif

    printf("Starting crash test for: %s\n"\
        "threads: %i\n" \
        "for %i minutes\n"
        "work files sizes: %i-%i Kb\n\nStarting threads...", root_dir, th_count, testing_time, file_min_size, file_max_size );

    time( &start );

    // creating threads arguments array, fill it, and start threads
    thread_arg_ptr threads = (thread_arg_ptr)malloc( sizeof(thread_arg) * th_count);

    statistic st;
    st.f_created = 0;
    st.f_erased = 0;
    st.d_created = 0;
    st.d_erased = 0;
    st.reading = 0;
    st.writing = 0;
    st.is_FS_full = false;

    // create & fill memory buffer for file I/O
    void *buffer = malloc( C_FILE_BUFFER_SIZE );
    for (i=0; i<C_FILE_BUFFER_SIZE; i++)
        ((char*)buffer)[i] = rand() & 0xFF;

    for (i=0; i<th_count; i++)
    {
        threads[i].th_num = i;
        threads[i].p_st = &st;
        threads[i].buffer = buffer;
        threads[i].state = PTH_INIT;
        threads[i].root_dir = root_dir;

        // starting working thread
        pthread_create( &threads[i].THREAD, 0, &thread_func, &threads[i] );
        //        pthread_t THREAD;
        //        int res = pthread_create( &THREAD, NULL, &thread_func, NULL );
        //        if (res)
        //        printf("Couldn't start child thread!\n");
    }

    //    printf("waiting for a key\n");
    //    getchar();
    //    printf("key was pressed\n");

    printf("OK!\n");
    do {
        time( &finish );
        char ctm[26];
        strcpy( ctm, ctime(&finish) );
        ctm[19] = 0x00;
        printf("Directories (M/E): %i/%i, Files: %i/%i, Data (R/W): %i/%i, %s\r", 
            st.d_created, st.d_erased,
            st.f_created, st.f_erased,
            st.reading, st.writing, &ctm[11] );
        sleep(1);

    } while ( difftime( finish, start )/60.0 < testing_time );

    // stopping all threads.
    printf("Exiting...\n");
    for (i=0; i<th_count; i++)
    {
        printf("Stopping thread: %i...", i);
        threads[i].state = PTH_STOP_PENDING;
        while (threads[i].state != PTH_EXITED);
        printf("OK!\n");
    }
    free(buffer);
    free(threads);

    return 0;
}
When in doubt, use brute force. © Ken Thompson

 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.