ioapps-1.4-r2/0000755000175000017500000000000012134167112012556 5ustar keruomkeruomioapps-1.4-r2/common.h0000644000175000017500000001335512134167044014232 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _COMMON_H_ #define _COMMON_H_ #include #include #include #include #include #include #include #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) // debugging macros so we can pin down message provenance at a glance #define WHERESTR "[%s:%s:%d]: " #define WHEREARG __FILE__, __FUNCTION__, __LINE__ #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) #ifndef NDEBUG #define DEBUGPRINT2(...) fprintf(stderr, __VA_ARGS__) #define DEBUGPRINTF(_fmt, ...) DEBUGPRINT2(WHERESTR _fmt, WHEREARG, __VA_ARGS__) #else #define DEBUGPRINTF(_fmt, ...) while(0) {} #endif #define ERRORPRINT2(...) fprintf(stderr, __VA_ARGS__) #define ERRORPRINTF(_fmt, ...) ERRORPRINT2("E!!" WHERESTR _fmt, WHEREARG, __VA_ARGS__) #define HASH_TABLE_SIZE 1019 #define MAX_STRING 512 #define MAX_LINE 512 #define MAX_DATA (1024*1024*64) #define MAX_TIME_STRING 20 #define MAX_PARENT_IDS 20 #define VERSION "1.4-r2" #define OFFSET_INVAL -1 #define SHM_KEY 0x00BEEF00 #define SHM_SIZE (10*1024*1024) #define OP_WRITE 'w' #define OP_PWRITE 'W' #define OP_READ 'r' #define OP_PREAD 'P' #define OP_ACCESS 'a' #define OP_UNKNOWN '?' #define OP_CLOSE 'c' #define OP_OPEN 'o' #define OP_CREAT 'R' #define OP_UNLINK 'u' #define OP_LSEEK 'l' #define OP_LLSEEK 'L' #define OP_PIPE 'p' #define OP_DUP 'd' #define OP_DUP2 'D' #define OP_DUP3 'e' #define OP_MKDIR 'M' #define OP_RMDIR 'i' #define OP_MMAP 'm' #define OP_CLONE 'C' #define OP_SOCKET 'S' #define OP_STAT 's' #define OP_SENDFILE 't' #define OP_FCNTL 'f' // Timing modes #define TIME_DIFF 0x80000000 ///< Try to hold the same difference between calls #define TIME_DIFF_STR "diff" #define TIME_EXACT 0x40000000 ///< Try to make calls in same time relative from start of the program #define TIME_EXACT_STR "exact" #define TIME_ASAP 0x20000000 ///< make calls as soon as possible #define TIME_ASAP_STR "asap" #define TIME_MASK 0xE0000000 #define ACT_MASK 0x000000FF #define ACT_CONVERT 0x1 #define ACT_SIMULATE 0x2 #define ACT_REPLICATE 0x4 #define ACT_STATS 0x8 #define ACT_CHECK 0x10 #define ACT_PREPARE 0x20 #define ACT_PRINT 0x40 #define FIX_MISSING 0x80 /** Our own version of struct timeval structure - the reason for it is to make sure it will be of equal size on both 32 and 64bit platforms. It will overflow in some 100 years, so we don't have to worry about it. Some attention should be given when using it with correct struct timeval structure. */ struct int32timeval { int32_t tv_sec; /* seconds */ int32_t tv_usec; /* microseconds */ }; /** Information common to all operations */ typedef struct op_info { int32_t pid; ///< pid of calling process int32_t dur; ///< duration of operation in us struct int32timeval start; ///< start of operation } op_info_t; typedef struct read_op { int32_t fd; int64_t size; int64_t retval; op_info_t info; } read_op_t; typedef struct write_op { int32_t fd; int64_t size; int64_t retval; op_info_t info; } write_op_t; typedef struct pread_op { int32_t fd; int64_t size; int64_t offset; int64_t retval; op_info_t info; } pread_op_t; typedef struct pwrite_op { int32_t fd; int64_t size; int64_t offset; int64_t retval; op_info_t info; } pwrite_op_t; typedef struct pipe_op { int32_t fd1; int32_t fd2; mode_t mode; int32_t retval; op_info_t info; } pipe_op_t; typedef struct mkdir_op { char name[MAX_STRING]; mode_t mode; int32_t retval; op_info_t info; } mkdir_op_t; typedef struct rmdir_op { char name[MAX_STRING]; int32_t retval; op_info_t info; } rmdir_op_t; typedef struct clone_op { mode_t mode; int32_t retval; op_info_t info; } clone_op_t; typedef struct dup_op { int32_t new_fd; int32_t old_fd; int32_t flags; int32_t retval; op_info_t info; } dup_op_t; typedef struct open_op { char name[MAX_STRING]; // we don't expect to have many open files, so we will not be allocating this manually int32_t flags; mode_t mode; int32_t retval; op_info_t info; } open_op_t; typedef struct close_op { int32_t fd; int32_t retval; op_info_t info; } close_op_t; typedef struct unlink_op { char name[MAX_STRING]; int32_t retval; op_info_t info; } unlink_op_t; typedef struct llseek_op { int32_t fd; int64_t offset; int64_t f_offset; ///< final offset in the file int32_t flag; int64_t retval; op_info_t info; } llseek_op_t; typedef struct lseek_op { int32_t fd; int32_t flag; int64_t offset; int64_t retval; ///< this one returns final offset as return value op_info_t info; } lseek_op_t; typedef struct access_op { char name[MAX_STRING]; mode_t mode; int32_t retval; op_info_t info; } access_op_t; typedef struct stat_op { char name[MAX_STRING]; int32_t retval; op_info_t info; } stat_op_t; typedef struct socket_op { int32_t retval; op_info_t info; } socket_op_t; typedef struct sendfile_op { int32_t out_fd; int32_t in_fd; int64_t offset; int64_t size; int64_t retval; op_info_t info; } sendfile_op_t; void * attach_sh_mem(); #endif ioapps-1.4-r2/print.c0000644000175000017500000002123612133726077014074 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "in_common.h" #define TIMEVAL_DIFF(t1, t2) (((uint64_t)(t1.tv_sec) * 1000000 + (uint64_t)(t1.tv_usec)) - ((uint64_t)(t2.tv_sec) * 1000000 + (uint64_t)(t2.tv_usec))) #define CALL_TIME(x) ((uint64_t)(x->o.info.start.tv_sec) * 1000000 + (uint64_t)(x->o.info.start.tv_usec)) #define DUR_TIME(x) ((uint32_t)(x->o.info.dur)) void print_read(read_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tread(%"PRIi32", addr, %"PRIi64") = %"PRIi64" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.fd,\ op_it->o.size, op_it->o.retval, op_it->o.info.dur); } void print_write(write_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\twrite(%"PRIi32", addr, %"PRIi64") = %"PRIi64" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.fd,\ op_it->o.size, op_it->o.retval, op_it->o.info.dur); } void print_pread(pread_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tpread(%"PRIi32", addr, %"PRIi64", %"PRIi64") = %"PRIi64" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.fd,\ op_it->o.size, op_it->o.offset, op_it->o.retval, op_it->o.info.dur); } void print_pwrite(pwrite_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tpwrite(%"PRIi32", addr, %"PRIi64", %"PRIi64") = %"PRIi64" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.fd,\ op_it->o.size, op_it->o.offset, op_it->o.retval, op_it->o.info.dur); } void print_pipe(pipe_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tpipe(%"PRIi32", %"PRIi32", 0x%"PRIx32") = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.fd1,\ op_it->o.fd2, op_it->o.mode, op_it->o.retval, op_it->o.info.dur); } void print_open(open_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\topen(%s, 0x%"PRIx32", 0x%"PRIx32") = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.name,\ op_it->o.flags, op_it->o.mode, op_it->o.retval, op_it->o.info.dur); } void print_clone(clone_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tclone(addr, addr, 0x%"PRIx32", args..) = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid,\ op_it->o.mode, op_it->o.retval, op_it->o.info.dur); } void print_close(close_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tclose(%"PRIi32") = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.fd,\ op_it->o.retval, op_it->o.info.dur); } void print_unlink(unlink_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tunlink(%s) = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.name,\ op_it->o.retval, op_it->o.info.dur); } void print_llseek(llseek_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tllseek(%"PRIi32", offset=%"PRIi64", result=%"PRIi64", %0x"PRIx32") = %"PRIi64" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.fd,\ op_it->o.offset, op_it->o.f_offset, op_it->o.flag, op_it->o.retval, op_it->o.info.dur); } void print_lseek(lseek_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tlseek(%"PRIi32", %"PRIi64", %0x"PRIx32") = %"PRIi64" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.fd,\ op_it->o.offset, op_it->o.flag, op_it->o.retval, op_it->o.info.dur); } void print_sendfile(sendfile_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tsendfile(%"PRIi32", %"PRIi32", %"PRIi64", %"PRIi64") = %"PRIi64" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.out_fd,\ op_it->o.in_fd, op_it->o.offset, op_it->o.size, op_it->o.retval, op_it->o.info.dur); } void print_mkdir(mkdir_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tmkdir(%s, 0x%"PRIx32") = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.name,\ op_it->o.mode, op_it->o.retval, op_it->o.info.dur); } void print_rmdir(rmdir_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\trmdir(%s) = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.name,\ op_it->o.retval, op_it->o.info.dur); } void print_dup(dup_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tdup2(%"PRIi32", %"PRIi32", 0x%"PRIx32") = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.old_fd,\ op_it->o.new_fd, op_it->o.flags, op_it->o.retval, op_it->o.info.dur); } void print_access(access_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\taccess(%s, 0x%"PRIx32") = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.name,\ op_it->o.mode, op_it->o.retval, op_it->o.info.dur); } void print_stat(stat_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tstat(%s, addr) = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid, op_it->o.name,\ op_it->o.retval, op_it->o.info.dur); } void print_socket(socket_item_t * op_it) { printf("%"PRIi32".%"PRIi32"\t%"PRIi32"\tsocket(domain, type, protocol) = %"PRIi32" <%"PRIi32">\n",\ op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.info.pid,\ op_it->o.retval, op_it->o.info.dur); } int print_items(list_t * list) { long long i = 0; item_t * item = list->head; common_op_item_t * com_it; while (item) { i++; com_it = list_entry(item, common_op_item_t, item); switch (com_it->type) { case OP_WRITE: print_write((write_item_t *) com_it); break; case OP_READ: print_read((read_item_t *) com_it); break; case OP_PWRITE: print_pwrite((pwrite_item_t *) com_it); break; case OP_PREAD: print_pread((pread_item_t *) com_it); break; case OP_OPEN: print_open((open_item_t *) com_it); break; case OP_CLOSE: print_close((close_item_t *) com_it); break; case OP_UNLINK: print_unlink((unlink_item_t *) com_it); break; case OP_LSEEK: print_lseek((lseek_item_t *) com_it); break; case OP_LLSEEK: print_llseek((llseek_item_t *) com_it); break; case OP_CLONE: print_clone((clone_item_t *) com_it); break; case OP_MKDIR: print_mkdir((mkdir_item_t *) com_it); break; case OP_RMDIR: print_rmdir((rmdir_item_t *) com_it); break; case OP_DUP: case OP_DUP2: case OP_DUP3: print_dup((dup_item_t *) com_it); break; case OP_PIPE: print_pipe((pipe_item_t *) com_it); break; case OP_ACCESS: print_access((access_item_t *) com_it); break; case OP_STAT: print_stat((stat_item_t *) com_it); break; case OP_SOCKET: print_socket((socket_item_t *) com_it); break; case OP_SENDFILE: print_sendfile((sendfile_item_t *) com_it); break; default: ERRORPRINTF("Unknown operation identifier: '%c'\n", com_it->type); return -1; break; } item = item->next; } return 0; } ioapps-1.4-r2/fdmap.h0000644000175000017500000000550012133726077014030 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _FDMAP_H_ #define _FDMAP_H_ #include "adt/hash_table.h" #include "common.h" #define FT_SPEC -1 ///< special file type for std in/ou/err (they are in fact reg. files) /** This structure serves for mapping among file descriptors of the original process and my filedescriptors, * as they may differ. */ typedef struct fd_map { int32_t my_fd; mode_t type; uint64_t cur_pos; ///< current position in the file. Usefull when simulating. struct int32timeval time_open; ///< when this file was opened char name[MAX_STRING]; ///< name of the file int created; ///< was it newly created or not? int32_t parent_fds[MAX_PARENT_IDS]; ///< array of parent fd numbers - usefull when deleting duplicated fd int32_t last_par_index; } fd_map_t; typedef struct fd_item { item_t item; int32_t old_fd; ///< key fd_map_t * fd_map; } fd_item_t; typedef struct fd_usage { item_t item; int32_t my_fd; ///< key int32_t usage; ///< how many processes has this fd in use } fd_usage_t; /** Structure used in the list of hashmaps of fd mappings for each process. */ typedef struct process_hash_item { item_t item; // I am part of the list hash_table_t * ht; // file descriptor table int32_t pid; } process_hash_item_t; hash_table_t * get_process_ht(hash_table_t * fd_mappings, int32_t pid); item_t * new_process_ht(int32_t pid); hash_table_t * duplicate_process_ht(hash_table_t * h, hash_table_t * usage_map); void delete_process_ht(hash_table_t * fd_mappings, int32_t pid); inline void increase_fd_usage(hash_table_t * h, int32_t fd); inline int decrease_fd_usage(hash_table_t * h, int32_t fd); inline fd_usage_t * new_fd_usage(); void delete_fd_usage(fd_usage_t * fd_usage); inline fd_item_t * new_fd_item(); void fd_item_remove_fd_map(item_t * item); void delete_fd_item(fd_item_t * item); void dump_fd_item(fd_item_t * fd_item); void dump_fd_list_item(item_t * it); void dump_process_hash_list_item(item_t * it); void insert_parent_fd(fd_item_t * fd_item, int32_t fd); int delete_parent_fd(fd_item_t * fd_item, int32_t fd); #endif ioapps-1.4-r2/ioreplay.h0000644000175000017500000000155512133726077014573 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _REPIO_H_ #define _REPIO_H_ #endif ioapps-1.4-r2/ioprofiler/0000755000175000017500000000000012134167012014727 5ustar keruomkeruomioapps-1.4-r2/ioprofiler/Makefile0000644000175000017500000000037612131056042016372 0ustar keruomkeruomPYTHON=python INSTALL?=install DOCDIR?=man MANDIR?=/usr/share/man/man1/ MANPAGE=ioprofiler.1 TARGET_PATH=/usr/bin/ all: python setup.py build install: python setup.py install $(INSTALL) $(DOCDIR)/$(MANPAGE) $(MANDIR) clean: python setup.py clean ioapps-1.4-r2/ioprofiler/man/0000755000175000017500000000000012134167012015502 5ustar keruomkeruomioapps-1.4-r2/ioprofiler/man/ioprofiler.10000644000175000017500000000152012134167044017741 0ustar keruomkeruom.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. .TH IOPROFILER "1" "April 2013" "IOProfiler v1.4-r2" "User Commands" .SH NAME IOProfiler \- IO traces profiler .SH SYNOPSIS .B ioprofiler .SH DESCRIPTION GUI application for IO profiling. Part of IOApps suite. It can read strace output files as well as binary files written by \fBioreplay\fR. For detailed information, real life examples and more see project home page: \fBhttps://code.google.com/p/ioapps/\fR .SH AUTHOR Written by Jiri Horky, 2010-2013 .SH COPYRIGHT Copyright \(co 2010 Jiri Horky .PP License GPLv2: GNU GPL version 2 .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" .BR ioreplay(1) ioapps-1.4-r2/ioprofiler/ioprofiler0000755000175000017500000005541112134167044017042 0ustar keruomkeruom#!/usr/bin/python # #IOApps, IO profiler and IO traces replayer # # Copyright (C) 2010 Jiri Horky # # 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. # # 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 Street, Fifth Floor, Boston, MA 02110-1301 USA # import sys from PyQt4 import QtCore from PyQt4.QtGui import * from PyQt4.QtCore import * import matplotlib.pyplot as plt from matplotlib.collections import LineCollection from matplotlib.ticker import FuncFormatter from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar from matplotlib.figure import Figure import numpy as np import ioapps import getopt #import cProfile from grapher import * #locale.setlocale(locale.LC_ALL, 'en_US') class MyDelegate(QStyledItemDelegate): def __init__(self, parent=None, *args): QStyledItemDelegate.__init__(self, parent, *args) def paint(self, painter, option, index): data = index.data() if data.type() == QVariant.Double: #if data.type() == QVariant.Date: painter.save() if option.state & QStyle.State_Selected: painter.fillRect(option.rect, option.palette.highlight()) pen = painter.pen() pen.setColor(QApplication.palette().color(QPalette.HighlightedText)) painter.setPen(pen) else: painter.fillRect(option.rect, QBrush(index.data(Qt.BackgroundRole))) val = data.toPyObject() if val > 1: formatString = "%0.1f" else: formatString = "%0.3f" text = locale.format(formatString, val, grouping=False) #text = formatString % val painter.drawText(option.rect, Qt.AlignCenter, text) painter.restore() else: QStyledItemDelegate.paint(self, painter, option, index); class DetailTable(QStandardItemModel): """ Table of operations """ #dataLoaded = QtCore.pyqtSignal() def __init__(self, *args): QStandardItemModel.__init__(self, *args) header = QStringList() header.append("Start[s]") header.append("Offset[kiB]") header.append("Size[kiB]") header.append("Dur[ms]") self.setHorizontalHeaderLabels(header) self.setSortRole(Qt.DisplayRole) def setData(self, dict): timeOpened = dict[0] data = dict[1] #rootItem = self.invisibleRootItem() #self.beginInsertRows(QModelIndex(), 0, len(data)-1) for item in data: myList = [] off = item[0] size = item[1] start = item[2] - timeOpened dur = item[3] stdItem = QStandardItem() a = QVariant(start) stdItem.setData(a, Qt.DisplayRole) myList.append(stdItem) stdItem = QStandardItem() a = QVariant(off) stdItem.setData(a, Qt.DisplayRole) myList.append(stdItem) stdItem = QStandardItem() a = QVariant(size) stdItem.setData(a, Qt.DisplayRole) myList.append(stdItem) stdItem = QStandardItem() a = QVariant(dur) stdItem.setData(a, Qt.DisplayRole) myList.append(stdItem) self.appendRow(myList) #self.dataLoaded.emit() self.emit(SIGNAL("dataLoaded"),()) class MyTableView(QTableView): expandableSection = 0 def __init__(self, parent = None): QTableView.__init__(self, parent) self.setSortingEnabled(True) self.setEditTriggers(QAbstractItemView.NoEditTriggers) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) def resizeEvent(self, event): self.setUpdatesEnabled(False) self.horizontalHeader().setStretchLastSection(False) #To make sure it don't get resized before we compute what we want self.horizontalHeader().resizeSections(QHeaderView.ResizeToContents) headerLen = self.horizontalHeader().length() sectionLen = self.horizontalHeader().sectionSize(self.expandableSection) viewLen = self.viewport().width() if headerLen < viewLen: self.horizontalHeader().setStretchLastSection(True) else: newLen = sectionLen - (headerLen - viewLen) self.horizontalHeader().resizeSection(self.expandableSection, newLen) self.setUpdatesEnabled(True) self.emit(SIGNAL("sizeChanged"),()) class MyTableWidget(QTableWidget): expandableSection = 0 dataSource = None def __init__(self, parent = None): QTableWidget.__init__(self, parent) self.setRowCount(1); self.setColumnCount(5); self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) header = QStringList() header.append("#Count") header.append("# ops") header.append("fileSize[MiB]") header.append("sumSize[MiB]") header.append("duration[s]") self.setHorizontalHeaderLabels(header) self.setMaximumHeight(60) self.setSortingEnabled(False) self.setEditTriggers(QAbstractItemView.NoEditTriggers) def resizeEvent(self, event): self.setUpdatesEnabled(False) self.horizontalHeader().setStretchLastSection(False) #To make sure it don't get resized before we compute what we want self.horizontalHeader().resizeSections(QHeaderView.ResizeToContents) headerLen = self.horizontalHeader().length() sectionLen = self.horizontalHeader().sectionSize(self.expandableSection) viewLen = self.viewport().width() if headerLen < viewLen: self.horizontalHeader().setStretchLastSection(True) else: newLen = sectionLen - (headerLen - viewLen) self.horizontalHeader().resizeSection(self.expandableSection, newLen) self.setUpdatesEnabled(True) def setDataSource(self, dataSource): self.dataSource = dataSource def recomputeData(self): ops = 0 fileSize = 0 sumSize = 0 dur = 0 count = self.dataSource.rowCount() for i in range(0, count): ops += int(self.dataSource.index(i, 1).data().toPyObject()) fileSize += float(self.dataSource.index(i, 2).data().toPyObject() / 1024) sumSize += float(self.dataSource.index(i, 3).data().toPyObject() / 1024) dur += float(self.dataSource.index(i, 4).data().toPyObject() / 1000) self.setItem(0,0, QTableWidgetItem(QString.number(count))); self.setItem(0,1, QTableWidgetItem(QString.number(ops))) self.setItem(0,2, QTableWidgetItem(QString.number(fileSize))) self.setItem(0,3, QTableWidgetItem(QString.number(sumSize))) self.setItem(0,4, QTableWidgetItem(QString.number(dur))) #class NameFilterProxyModel(QSortFilterProxyModel): # def __init(self, column = 0, parent = None): # QSortFilterProxyModel.__init__(self,parent) # int source_row, const QModelIndex & source_parent # def filterAcceptsRow(self, row, parent): class DetailDialog(QDialog): reads = "reads"; writes = "writes"; def __init__(self, fileName, reads, dataHolder, parent = None): QDialog.__init__(self, parent); if reads: self.mode = self.reads; else: self.mode = self.writes; self.fileName = fileName self.dataHolder = dataHolder self.setWindowTitle(fileName + " - " + self.mode) self.grapher = SubGrapher() if reads: self.grapher.setType(self.grapher.READS) else: self.grapher.setType(self.grapher.WRITES) self.grapher.setName(self.fileName) self.grapher.setTimeMode('relfile') self.grapher.setDataHolder(dataHolder) self.highlightedItems = [] self.standardBrush = None # Create the mpl Figure and FigCanvas objects. # 5x4 inches, 72 dots-per-inch # self.dpi = 72 self.fig = Figure((6.0, 5.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) #our table: self.model = None self.view = QTableView() self.view.setSortingEnabled(True) self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.view.horizontalHeader() self.view.setSelectionBehavior(QAbstractItemView.SelectRows) delegate = MyDelegate() self.view.setItemDelegate(delegate) self.view.setMinimumWidth(415) self.view.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) # Since we have only one plot, we can use add_axes # instead of add_subplot, but then the subplot # configuration tool in the navigation toolbar wouldn't # work. # self.axes = self.fig.add_subplot(111) self.grapher.setAxes(self.axes) self.grapher.setFig(self.fig) self.canvas.mpl_connect('pick_event', self.on_pick) self.mpl_toolbar = NavigationToolbar(self.canvas, self) # Other GUI controls # self.draw_button = QPushButton("&Highlight selected") QObject.connect(self.draw_button, SIGNAL("clicked()"), self.__highlightSelected) QObject.connect(self.draw_button, SIGNAL("clicked()"), self.__highlightSelected) self.displayStatistics = QCheckBox("Display statistics") QObject.connect(self.displayStatistics, SIGNAL("stateChanged(int)"), self.__showPlot) plot_label = QLabel('Type of plot:') self.patternButton = QRadioButton("Pattern diagram") self.histSizeButton = QRadioButton("Size histogram") self.histTimeButton = QRadioButton("Time histogram") self.histSpeedButton = QRadioButton("Speed histogram") self.patternButton.setChecked(True) QObject.connect(self.patternButton, SIGNAL("toggled(bool)"), self.__showPlot) QObject.connect(self.histSizeButton, SIGNAL("toggled(bool)"), self.__showPlot) QObject.connect(self.histTimeButton, SIGNAL("toggled(bool)"), self.__showPlot) QObject.connect(self.histSpeedButton, SIGNAL("toggled(bool)"), self.__showPlot) # # Layout with box sizers # hbox = QHBoxLayout() hbox.addWidget(self.draw_button) hbox.addWidget(self.displayStatistics); hbox.addStretch() for w in [plot_label, self.patternButton, self.histSizeButton, self.histTimeButton, self.histSpeedButton]: hbox.addWidget(w) hbox.setAlignment(w, Qt.AlignVCenter) vvbox = QVBoxLayout() vvbox.addWidget(self.canvas) vvbox.addWidget(self.mpl_toolbar) hhbox = QHBoxLayout() hhbox.addWidget(self.view) hhbox.addLayout(vvbox) vbox = QVBoxLayout() vbox.addLayout(hhbox) vbox.addLayout(hbox) self.setLayout(vbox) self.__showPlot() def __clearHighlighted(self): if self.standardBrush: for item in self.highlightedItems: item.setBackground(self.standardBrush) def __highlightRows(self, rows): if self.standardBrush == None: #this is the first time we are called self.standardBrush = self.view.model().item(0,0).background() l = [] brush = QBrush(Qt.red) for row in rows: for i in xrange(self.view.model().columnCount()): item = self.view.model().item(row, i) item.setBackground(brush) l.append(item); return l def __highlightSelected(self): self.__clearHighlighted(); indexes = self.view.selectedIndexes() rows = [] for index in indexes: rows.append(index.row()) rowset = set(rows); self.highlightedItems = self.__highlightRows(rowset) self.__highlightLines(rowset, self.grapher.lineCollection()) self.__highlightRows(rowset) self.canvas.draw() def __highlightLines(self, lines, artist): cmap = [] for i in xrange(len(artist.get_paths())): if i in lines: cmap.append('r') else: cmap.append('b') artist.set_color(cmap) def setModel(self, model): self.model = model self.connect(self.model, SIGNAL("dataLoaded()"), self.view.resizeColumnsToContents) self.connect(self.model, SIGNAL("dataLoaded()"), self.view.resizeRowsToContents) #model.dataLoaded.connect(self.view.resizeColumnsToContents) #model.dataLoaded.connect(self.view.resizeRowsToContents) self.view.setModel(self.model) self.view.resizeColumnsToContents() self.view.resizeRowsToContents() def on_pick(self, event): # The event received here is of the type # matplotlib.backend_bases.PickEvent # # It carries lots of information, of which we're using # only a small amount here. # if not self.pickEnabled: return; indexes = [] print "event ind is", event.ind for i in event.ind: path = event.artist.get_paths()[i] for t in path.iter_segments(): startTime = t[0][1] / 1000 modelIndex = self.view.model().index(0,0) startTime = str(startTime)[:7] inds = self.view.model().match(modelIndex, Qt.DisplayRole, startTime) indexes += inds break; #we know that the Path consists by one line, so this is sufficient #highlight it self.__highlightLines(event.ind, event.artist) self.view.scrollTo(indexes[0], QAbstractItemView.PositionAtCenter) self.__clearHighlighted(); rows = [] for index in indexes: row = index.row() rows.append(row) self.highlightedItems = self.__highlightRows(set(rows)) self.canvas.draw() def plotData(self): self.grapher.setNames(self.fileName) if self.mode == self.reads: self.grapher.plotReads() else: self.grapher.plotWrites() def __showPlot(self): """ Redraws the figure """ if self.patternButton.isChecked(): self.pickEnabled = True if self.mode == self.reads: self.grapher.plotReads(self.displayStatistics.isChecked()) else: self.grapher.plotWrites(self.displayStatistics.isChecked()) elif self.histSizeButton.isChecked(): self.grapher.plotHistSize(self.displayStatistics.isChecked()) self.pickEnabled = False self.__clearHighlighted() elif self.histTimeButton.isChecked(): self.grapher.plotHistTime(self.displayStatistics.isChecked()) self.pickEnabled= False self.__clearHighlighted() elif self.histSpeedButton.isChecked(): self.grapher.plotHistSpeed(self.displayStatistics.isChecked()) self.pickEnabled= False self.__clearHighlighted() self.canvas.draw() class CentralWidget(QWidget): READS = "reads" WRITES = "writes" def printing(self, a, b): print "changed" def __init__(self, parent = None): QWidget.__init__(self, parent) self.connect(QCoreApplication.instance(), SIGNAL("aboutToQuit()"), self.__clean) #QCoreApplication.instance().aboutToQuit.connect(self.__clean) self.showing = None self.fileName = None self.dialogs = [] self.dataHolder = DataHolder() self.model = MyTable(0,0, self) self.filter = QSortFilterProxyModel(self) self.filter.setSourceModel(self.model) self.tableW = MyTableWidget(self) self.tableW.setDataSource(self.filter) #self.setSelectionBehavior(QAbstractItemView.SelectRows) lineEdit = QLineEdit('Name regexp filter', self) self.connect(lineEdit, SIGNAL("textChanged(QString)"), self.filter.setFilterRegExp) #lineEdit.textChanged.connect(self.filter.setFilterRegExp) self.view = MyTableView() self.view.setModel(self.filter) #self.view.horizontalHeader().setStretchLastSection(True) delegate = MyDelegate() self.view.setItemDelegate(delegate) self.buttonD = QPushButton("Details", self) layout = QHBoxLayout() self.readRadioButton = QRadioButton("Reads") self.writeRadioButton = QRadioButton("Writes") self.connect(self.readRadioButton, SIGNAL("toggled(bool)"), self.__showTable) self.connect(self.writeRadioButton, SIGNAL("toggled(bool)"), self.__showTable) #self.readRadioButton.toggled.connect(self.__showTable) #self.writeRadioButton.toggled.connect(self.__showTable) layout.addWidget(self.readRadioButton) layout.addWidget(self.writeRadioButton) self.readRadioButton.setChecked(True) layout.addWidget(self.buttonD) self.connect(self.buttonD, SIGNAL("clicked()"), self.showDetails) #buttonD.clicked.connect(self.showDetails) vlayoutMain = QVBoxLayout(self) vlayoutMain.addWidget(self.view) vlayoutMain.addWidget(self.tableW) vlayoutMain.addWidget(lineEdit) vlayoutMain.addLayout(layout) def __clean(self): for diag in self.dialogs: diag.close(); def showDetails(self): indexes = self.view.selectedIndexes() rows = [] for index in indexes: rows.append(self.filter.mapToSource(index).row()) rowset = set(rows) if not len(rowset) == 1: QMessageBox.warning(self, "Warning", "Exactly one file has to be specified.", ); return fileName = self.model.getNames(rowset)[0] if self.readRadioButton.isChecked(): data = self.dataHolder.data()[self.READS][fileName] reads = True else: data = self.dataHolder.data()[self.WRITES][fileName] reads = False dialog = DetailDialog(fileName, reads, self.dataHolder, self) model = DetailTable(0,0, self) model.setData(data) dialog.setModel(model) dialog.show() self.dialogs.append(dialog) self.connect(dialog, SIGNAL("finished(int)"), self.__dialogClosed) #dialog.finished.connect(self.__dialogClosed) def __dialogClosed(self, retVal): dial = self.sender() self.dialogs.remove(dial) def newFileOpened(self, fileName): if not self.fileName == None: model = self.filter.sourceModel(); # detach for performance reasons modelView = self.view.model(); self.filter.setSourceModel(None) self.view.setModel(None) model.clear() #clear self.filter.setSourceModel(model) self.view.setModel(modelView) # attach again ioapps.finish() self.fileName = fileName if fileName.endsWith(".bin"): ioapps.init_items_bin(fileName.toLocal8Bit().data()); else: ioapps.init_items_strace(fileName.toLocal8Bit().data()); ioapps.simulate(); ioapps.free_items(); reads = ioapps.get_reads() writes = ioapps.get_writes() self.dict = {} self.dict[self.READS] = reads; self.dict[self.WRITES] = writes; self.dataHolder.setData(self.dict) self.__showTable() self.tableW.recomputeData() self.filter.rowsRemoved.connect(self.tableW.recomputeData) self.filter.rowsInserted.connect(self.tableW.recomputeData) def __showTable(self): if not self.fileName: return if self.readRadioButton.isChecked(): if self.showing == self.READS: #already showing reads return sumDict = self.dataHolder.getSummary(self.READS) self.showing = self.READS else: if self.showing == self.WRITES: return self.showing = self.WRITES sumDict = self.dataHolder.getSummary(self.WRITES) model = None if not self.showing == None: model = self.filter.sourceModel() self.filter.setSourceModel(None) #for performance reasons self.view.setModel(None) #detach model completely for performance reasons? self.model.clear() self.showing = None for k,v in sumDict.iteritems(): list = [k] + v self.model.addRow(list) if self.filter.sourceModel() == None: self.filter.setSourceModel(model) if self.view.model() == None: self.view.setModel(self.filter) self.tableW.recomputeData() class MainWindow(QMainWindow): appName = "IOprofiler - IOapps suite" def __init__(self, parent = None): QMainWindow.__init__(self, parent) self.fileName = None self.resize(800, 600) self.setWindowTitle(self.appName) open = QAction(QIcon('icons/open.png'), '&Open', self) open.setShortcut('Ctrl+O') open.setStatusTip('Load file containing recording traces') self.connect(open, SIGNAL('triggered()'), self.slotFile) exit = QAction(QIcon('icons/exit.png'), '&Exit', self) exit.setShortcut('Ctrl+W') exit.setStatusTip('Exit application') self.connect(exit, SIGNAL('triggered()'), self.close) self.setWindowIcon(QIcon('/usr/kde/3.5/share/icons/default.kde/64x64/devices/hdd_unmount.png')) self.statusBar() menubar = self.menuBar() file = menubar.addMenu('&File') file.addAction(open) file.addAction(exit) aboutQt = QAction(QIcon('icons/about.png'), '&About Qt', self) aboutQt.setStatusTip('Show about Qt dialog') self.connect(aboutQt, SIGNAL('triggered()'), self.slotAboutQt) self.centralW = CentralWidget() self.setCentralWidget(self.centralW) ioHelp = QAction(QIcon('icons/help.png'), '&Help', self) ioHelp.setShortcut('F1') ioHelp.setStatusTip('Displays a brief help of the program') self.connect(ioHelp, SIGNAL('triggered()'), self.slotIoHelp) help = menubar.addMenu('&Help') help.addAction(ioHelp) help.addAction(aboutQt) if len(sys.argv) > 1: self.fileName = QString(sys.argv[1]) self.slotFile(self.fileName) def slotFile(self, fileName=None): print fileName if not fileName: fileName = QFileDialog.getOpenFileName(self, "", "~/", "Repio binary files (*.bin);;Pure strace output (*)") print fileName if fileName: self.fileName = fileName self.updateWindowTitle() self.centralW.newFileOpened(self.fileName) def slotAboutQt(self): QMessageBox.aboutQt(self) def slotIoHelp(self): QMessageBox.about(self, "Help", "IOprofiler v1.4-r2 - part of the IOapps suite
\ https://code.google.com/p/ioapps/
\ Written by Jiri Horky.

\ GUI application for IO profiling. It can read strace output as well as binary traces converted by ioreplay application.
\ You can get traces of your applications by running a wrapper script:
\
\ ioprofiler-trace <program> <arguments>
\
\ which will produce ioproftrace.log file.
\ See the project web page for more examples and description."); def updateWindowTitle(self): if self.fileName: self.setWindowTitle(self.appName + " - " + self.fileName) class MyTable(QStandardItemModel): """ Table of operations """ def __init__(self, *args): QStandardItemModel.__init__(self, *args) self.setSortRole(Qt.DisplayRole) self.__setHeader() def __setHeader(self): header = QStringList() header.append("Name") header.append("# ops") header.append("fileSize[kiB]") header.append("sumSize[kiB]") header.append("Dur[ms]") self.setHorizontalHeaderLabels(header) def clear(self): QStandardItemModel.clear(self) self.__setHeader() def addRow(self, list): myList = [] for item in list: #print "item", i, item stdItem = QStandardItem() if type(item) in [np.float64, np.float64]: # as of PyQt 4.7, we have specifically convert numpy.float* numbers to python's float, to make sure QVariant understands it a = QVariant(float(item)) else: a = QVariant(item) stdItem.setData(a, Qt.DisplayRole) myList.append(stdItem) self.appendRow(myList) def getNames(self, rows): names = [] for row in rows: names.append(self.item(row).data(Qt.DisplayRole).toPyObject().toLocal8Bit().data()) return names if __name__ == "__main__": app = QApplication(sys.argv) main = MainWindow() main.show() app.exec_() #cProfile.run('app.exec_()', '/tmp/profile') sys.exit(0) sys.exit(app.exec_()) ioapps-1.4-r2/ioprofiler/setup.py0000755000175000017500000000321612134167044016453 0ustar keruomkeruom#!/usr/bin/python # IOapps, IO profiler and IO traces replayer # # Copyright (C) 2010 Jiri Horky # # 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. # # 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 Street, Fifth Floor, Boston, MA 02110-1301 USA from distutils.core import setup, Extension setup(name="ioprofiler", version="1.4-r2", description="IO profiler, part of IOApps", author="Jiri Horky", author_email="jiri.horky@gmail.com", url="http://code.google.com/p/ioapps/", package_data = [ "README", "../COPYING" ], ext_modules = [Extension("ioapps", define_macros = [('_GNU_SOURCE', None), ('_FILE_OFFSET_BITS',64), ('PY_MODULE', None)], extra_compile_args = ["-g"], sources = ["ioappsmodule.c", "../in_common.c", "../in_binary.c", "../in_strace.c", "../adt/list.c", "../adt/hash_table.c", "../namemap.c", "../simulate.c", "../replicate.c", "../fdmap.c", "../stats.c", "../simfs.c", "../adt/fs_trie.c"], include_dirs = ['../'])], py_modules = [ 'grapher' ], scripts=['ioprofiler'] ) ioapps-1.4-r2/ioprofiler/ioappsmodule.c0000644000175000017500000002001512133726077017605 0ustar keruomkeruom/* IOtool, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../common.h" #include "../adt/list.h" #include "../simulate.h" #include "../replicate.h" #include "../in_binary.h" #include "../in_strace.h" list_t * list_g = NULL; PyObject * read_dict_g = NULL; PyObject * write_dict_g = NULL; /* The module doc string */ PyDoc_STRVAR(ioapps__doc__, "Repio module. It benefits from ioapps C program (loading, normalizing functions)."); /* The function doc strings */ PyDoc_STRVAR(init_items_bin__doc__, "Arguments: filename. Loads syscalls from file in binary form. Sets context for other calls."); PyDoc_STRVAR(init_items_strace__doc__, "Arguments: filename. Loads syscalls from file in strace form. Sets context for other calls."); PyDoc_STRVAR(simulate__doc__, "Arguments: none. Prepares structures for get_reads and get_writes functions."); PyDoc_STRVAR(free_items__doc__, "Arguments: none. Remove previously allocated items."); PyDoc_STRVAR(get_reads__doc__, "Arguments: none. Return a list of reads per file."); PyDoc_STRVAR(get_writes__doc__, "Arguments: none. Return a list of writes per file."); PyDoc_STRVAR(finish__doc__, "Arguments: none. Deallocates all stuctures."); static PyObject * init_items_bin(PyObject *self, PyObject *args) { char * filename; if (! list_g) { list_g = malloc(sizeof(list_t)); list_init(list_g); } else { PyErr_SetString(PyExc_ValueError, "List of syscalls already initialized!"); return NULL; } /* The ':init_items_binary' is for error messages */ if (!PyArg_ParseTuple(args, "s:init_items_binary", &filename)) return NULL; /* Call the C function */ if (bin_get_items(filename, list_g)) { PyErr_SetString(PyExc_ValueError, "Error loading list of syscalls."); return NULL; } return Py_None; } static PyObject * init_items_strace(PyObject *self, PyObject *args) { char * filename; if (! list_g) { list_g = malloc(sizeof(list_t)); list_init(list_g); } else { PyErr_SetString(PyExc_ValueError, "List of syscalls already initialized!"); return NULL; } /* The ':init_items_strace' is for error messages */ if (!PyArg_ParseTuple(args, "s:init_items_strace", &filename)) return NULL; /* Call the C function */ if (strace_get_items(filename, list_g, 0)) { PyErr_SetString(PyExc_ValueError, "Error loading list of syscalls."); return NULL; } return Py_None; } static PyObject * simulate(PyObject *self, PyObject *args) { if (! list_g) { PyErr_SetString(PyExc_ValueError, "List of syscalls not initialized!"); return NULL; } simulate_init(ACT_SIMULATE); /* Call the C function */ if (replicate(list_g, 0, 1, ACT_SIMULATE | FIX_MISSING, NULL, NULL)) { PyErr_SetString(PyExc_ValueError, "Error simulating of syscalls."); return NULL; } return Py_None; } PyObject * make_pylist_from_list(list_t * list) { PyObject * tuple; rw_op_t * rw_op; item_t * cur = list->head; PyObject * pylist; double starttime, dur; double size; double offset; if ( (pylist = PyList_New(0)) == NULL) { PyErr_SetString(PyExc_ValueError, "Cannot create python list!"); return NULL; } int i = 0; while(cur) { rw_op = list_entry(cur, rw_op_t, item); starttime = rw_op->start.tv_sec; starttime += rw_op->start.tv_usec / 1000000.0L; dur = rw_op->dur / 1000.0L; offset = rw_op->offset / (1024.0); size = rw_op->size / (1024.0); tuple = Py_BuildValue("(dddd)", offset, size, starttime, dur); if ( PyList_Append(pylist, tuple) ) { return NULL; } cur = cur->next; i++; } return pylist; } void make_keyval_from_item_read(item_t * item) { PyObject * tuple; if (PyErr_Occurred()) return; sim_item_t * sim_item = hash_table_entry(item, sim_item_t, item); PyObject * list = make_pylist_from_list(&sim_item->list); if (list) { double time_open = sim_item->time_open.tv_sec; time_open += sim_item->time_open.tv_usec / 1000000.L; if ( (tuple = Py_BuildValue("(dO)", time_open, list)) == NULL ) return; PyDict_SetItemString(read_dict_g, sim_item->name, tuple); } } void make_keyval_from_item_write(item_t * item) { PyObject * tuple; if (PyErr_Occurred()) return; sim_item_t * sim_item = hash_table_entry(item, sim_item_t, item); PyObject * list = make_pylist_from_list(&sim_item->list); if (list) { double time_open = sim_item->time_open.tv_sec; time_open += sim_item->time_open.tv_usec / 1000000.L; if ( (tuple = Py_BuildValue("(dO)", time_open, list)) == NULL ) return; PyDict_SetItemString(write_dict_g, sim_item->name, tuple); } } static PyObject * get_reads(PyObject *self, PyObject *args) { hash_table_t * ht; /* Call the C function */ if ( (ht = simulate_get_map_read()) == NULL) { PyErr_SetString(PyExc_ValueError, "Error getting simulation data, did you forget to call simulate?"); return NULL; } if ( ! read_dict_g ) { read_dict_g = PyDict_New(); } else { return read_dict_g; } hash_table_apply(ht, make_keyval_from_item_read); if (PyErr_Occurred()) { return NULL; } else { return read_dict_g; } } static PyObject * get_writes(PyObject *self, PyObject *args) { hash_table_t * ht; /* Call the C function */ if ((ht = simulate_get_map_write()) == NULL) { PyErr_SetString(PyExc_ValueError, "Error getting simulation data, did you forget to call simulate?"); return NULL; } if ( ! write_dict_g ) { write_dict_g = PyDict_New(); } else { return write_dict_g; } hash_table_apply(ht, make_keyval_from_item_write); if (PyErr_Occurred()) { return NULL; } else { return write_dict_g; } } static PyObject * free_items(PyObject *self, PyObject *args) { /* Call the C function */ if (remove_items(list_g)) { PyErr_SetString(PyExc_ValueError, "Error removing list of syscalls."); return NULL; } free(list_g); list_g = NULL; return Py_None; } static PyObject * finish(PyObject *self, PyObject *args) { simulate_finish(); //I am not sure whether this will lead to memory leak or not... PyDict_Clear(read_dict_g); PyDict_Clear(write_dict_g); read_dict_g = NULL; write_dict_g = NULL; return Py_None; } /* A list of all the methods defined by this module. */ /* "iterate_point" is the name seen inside of Python */ /* "py_iterate_point" is the name of the C function handling the Python call */ /* "METH_VARGS" tells Python how to call the handler */ /* The {NULL, NULL} entry indicates the end of the method definitions */ static PyMethodDef ioapps_methods[] = { {"init_items_bin", init_items_bin, METH_VARARGS, init_items_bin__doc__}, {"init_items_strace", init_items_strace, METH_VARARGS, init_items_strace__doc__}, {"free_items", free_items, METH_VARARGS, free_items__doc__}, {"simulate", simulate, METH_VARARGS, simulate__doc__}, {"finish", finish, METH_VARARGS, finish__doc__}, {"get_reads", get_reads, METH_VARARGS, get_reads__doc__}, {"get_writes", get_writes, METH_VARARGS, get_writes__doc__}, {NULL, NULL} /* sentinel */ }; /* When Python imports a C module named 'X' it loads the module */ /* then looks for a method named "init"+X and calls it. Hence */ /* for the module "mandelbrot" the initialization function is */ /* "initmandelbrot". The PyMODINIT_FUNC helps with portability */ /* across operating systems and between C and C++ compilers */ PyMODINIT_FUNC initioapps(void) { /* There have been several InitModule functions over time */ Py_InitModule3("ioapps", ioapps_methods, ioapps__doc__); } ioapps-1.4-r2/ioprofiler/grapher.py0000644000175000017500000002114512133726077016747 0ustar keruomkeruom#IOApps, IO profiler and IO traces replayer # # Copyright (C) 2010 Jiri Horky # # 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. # # 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 Street, Fifth Floor, Boston, MA 02110-1301 USA # import matplotlib.pyplot as plt from matplotlib.collections import LineCollection from matplotlib.ticker import FuncFormatter from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar from matplotlib.figure import Figure import numpy as np import math import os import locale def percentile(N, percent, key=lambda x:x): """ Find the percentile of a list of values. @parameter N - is a list of values. Note N MUST BE already sorted. @parameter percent - a float value from 0.0 to 1.0. @parameter key - optional key function to compute value from each element of N. @return - the percentile of the values """ if N == None: return None k = (len(N)-1) * percent f = math.floor(k) c = math.ceil(k) if f == c: return key(N[int(k)]) d0 = key(N[int(f)]) * (k-f) d1 = key(N[int(c)]) * (c-k) return d0+d1 class DataHolder: def __init__(self): self.dict = None def setData(self, dict): self.dict = dict def data(self): return self.dict def getSummary(self, type): if not self.dict: raise ValueError('Data not set') retDict = {} for name in self.dict[type].keys(): ops = self.dict[type][name][1] data = np.array(ops, dtype=[('off', np.float64), ('size', np.float64), ('start', np.float64), ('dur', np.float64)]) retDict[name] = [len(ops), max(data['size'] + data['off']), sum(data['size']), sum(data['dur'])] return retDict class SubGrapher: """Can graph....""" WRITES = 'writes' READS = 'reads' MAX_NAME = 80 #should be always divisible by 2 timeModes = ['absolute', 'relapp', 'relfile', 'count'] plotModes = ['show', 'save'] def __init__(self): self.fileName = None self.data = None self.type = self.READS self.timeMode = self.timeModes[0] self.plotMode = self.plotModes[0] self.firtAppTime = 0 self.lineCol = None self.dpi = 72 self.axes = None self.fig = None self.dataHolder = None def __prepareData(self): if self.timeMode == "relapp": self.firstAppTime = self.dataHolder.data()[self.type][self.fileName][0][2] firstFileTime = self.dataHolder.data()[self.type][self.fileName][0] * 1000 #reads ops = self.dataHolder.data()[self.type][self.fileName][1] self.data = np.array(ops, dtype=[('off', np.float64), ('size', np.float64), ('start', np.float64), ('dur', np.float64)]) self.data['start'] = self.data['start'] * 1000; if self.timeMode == "relfile": self.startTime = 0 self.data['start'] -= firstFileTime; elif self.timeMode == "relapp": self.startTime = 0 self.data['start'] -= self.firstAppTime; elif self.timeMode == "absolute": self.startTime = self.data['start'][0] else: self.startTime = 0 def __xFormater(self, x, pos): """Formatter for X axis""" return locale.format("%0.1f", x, grouping=True) def __elideText(self, text): if len(text) > self.MAX_NAME: return text[0:(self.MAX_NAME/2-2)] + "...." + text[-(self.MAX_NAME/2-2):] else: return text def __getTicks(self, low, high): minTicks = 10 maxTicks = 20 ticks = [] div = int(1) pow = 0 while high / div > maxTicks: pow+=1 div*=10 tick = 0; while tick < high: ticks.append(tick) tick += div if pow == 0: last = round(high) else: last = round(high + div/2.0, 0 - pow) ticks.append(last) return ticks def setTimeMode(self, mode): if mode in self.timeModes: self.timeMode = mode; else: print 'Time mode not in allowed list' def timeMode(self): return self.timeMode def setFig(self, fig): self.fig = fig def setAxes(self, axes): self.axes = axes def setDataHolder(self, dataHolder): self.dataHolder = dataHolder def dataHolder(self): return self.dataHolder def setPlotMode(self, mode): if mode in self.plotModes: self.plotMode = mode; else: print 'Plot mode not in allowed list' def setName(self, name): self.fileName = name def name(self): return self.fileName def setType(self, type): self.type = type def plotMode(self): return self.plotMode def filenames(self, type): return self.dataHolder.data()[type].keys() def lineCollection(self): return self.lineCol def plotHist(self, stats, data, xlabel, ylabel): self.axes.clear() if stats: self.__printStats() mean = np.mean(data, dtype=np.float64); std = np.std(data, dtype=np.float64); inRange = 0 step = 2 while inRange < np.size(data)*0.99: # make sure 99% of data are displayed rangeMin = mean-step*std rangeMax = mean+step*std if rangeMin < 0: rangeMin = 0 inRange = ((rangeMin <= data) & (data <= rangeMax)).sum() step = step*2 self.axes.hist(data, 50, facecolor='green', range=(rangeMin, rangeMax)) self.axes.set_xlabel(xlabel) self.axes.set_ylabel(ylabel) def __printStats(self): sizeMean = np.mean(self.data['size'], dtype=np.float64); sizeStd = np.std(self.data['size'], dtype=np.float64); durMean = np.mean(self.data['dur'], dtype=np.float64); durStd = np.std(self.data['dur'], dtype=np.float64); sumSize = sum(self.data['size']) sumDur = sum(self.data['dur']) text = "Total %0.1lf kB in %ld calls (avg. %0.1f kiB/s)\n Size avg[kiB] %0.1f +- %0.1f, max: %0.1lf, min: %0.1lf\n Dur[ms]: Total: %0.3lf, Avg: %0.3f +- %0.3lf, max: %0.3lf, min: %0.3lf"\ % (sumSize, len(self.data['size']), (sumSize/sumDur)*1000, sizeMean, sizeStd, max(self.data['size']), min(self.data['size']), sumDur, durMean, durStd, max(self.data['dur']), min(self.data['size'])) self.axes.text(0.95, 0.05, text, horizontalalignment='right', verticalalignment='bottom', transform=self.axes.transAxes, fontsize=12) def plotHistSize(self, stats): if self.data == None: self.__prepareData self.plotHist(stats, self.data['size'], "request size (kiB)", "count") def plotHistTime(self, stats): if self.data == None: self.__prepareData self.plotHist(stats, self.data['dur'], "request duration (ms)", "count") def plotHistSpeed(self, stats): if self.data == None: self.__prepareData self.plotHist(stats, self.data['size']/self.data['dur']*1000, "request speed (kiB/s)", "count") def plotPattern(self, type, stats): """Common graph function for read and writes, type is either 'read' or 'write'. Stats is a bool that indicates whether to print statistics or not.""" names = self.filenames(type) #unique names of used files if not self.fileName in names: print self.fileName, "is not in our data set" return if self.data == None: self.__prepareData() self.axes.clear() graphdata = np.column_stack((self.data['off'], self.data['start'], self.data['off']+ self.data['size'], self.data['start'] + self.data['dur'])) lineSegments = LineCollection(graphdata.reshape(-1,2,2), linewidths=(4)); lineSegments.set_picker(True) self.lineCol = self.axes.add_collection(lineSegments) maxEnd = max(graphdata[:,2]) maxTime = max(graphdata[:,3]) if stats: self.__printStats() self.axes.xaxis.set_major_formatter(FuncFormatter(self.__xFormater)) self.axes.grid(color='grey', linewidth=0.5) self.axes.set_xlabel("file offset (kiB)", fontsize=16); self.axes.set_ylabel("time (ms)", fontsize=16); self.axes.set_xlim(0, maxEnd); self.axes.set_ylim(self.startTime, maxTime); self.fig.suptitle('%s' % self.__elideText(self.fileName), fontsize=9) # ticks = self.__getTicks(0, maxEnd) # plt.xticks(ticks); self.fig.autofmt_xdate() def plotReads(self, stats): self.plotPattern(self.READS, stats) def plotWrites(self, stats): self.plotPattern(self.WRITES, stats) class Grapher(SubGrapher): """Can graph....""" def __init__(self): SubGrapher.__init__() fig = plt.figure() ax = plt.axes() self.setFig(fig) self.setAxes(ax) def plot(self, type): SubGrapher.plot(self, type) plt.show(); ioapps-1.4-r2/simulate.h0000644000175000017500000000462212133726077014570 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SIMULATE_H_ #define _SIMULATE_H_ /** @file simulate.h * * Takes care of noting of every read and write operation, based on the filename. * * It uses hash_table, where the key is a string and data are sim_item_t strutcs, which for every filename contain * list of operations (struct rw_op). */ #include #include "fdmap.h" #include "in_common.h" #include "adt/hash_table.h" typedef struct rw_op { item_t item; int64_t offset; uint64_t size; struct int32timeval start; ///< start of operation int32_t dur; ///< duration of operation } rw_op_t; typedef struct sim_item_t { item_t item; char name[MAX_STRING]; int created; struct int32timeval time_open; list_t list; } sim_item_t; hash_table_t * simulate_get_map_read(); hash_table_t * simulate_get_map_write(); inline int simulate_get_open_fd(); inline void simulate_sendfile(fd_item_t * in_fd_item, fd_item_t * out_fd_item, sendfile_item_t * op_it); inline void simulate_read(fd_item_t * fd_item, read_item_t * op_it); inline void simulate_write(fd_item_t * fd_item, write_item_t * op_it); inline void simulate_pread(fd_item_t * fd_item, pread_item_t * op_it); inline void simulate_pwrite(fd_item_t * fd_item, pwrite_item_t * op_it); void simulate_access(access_op_t * op_it); void simulate_stat(stat_op_t * op_it); void simulate_mkdir(mkdir_op_t * op_it); void simulate_rmdir(rmdir_op_t * op_it); void simulate_unlink(unlink_op_t * op_it); void simulate_creat(open_op_t * op_it); void simulate_init(int mode); void simulate_finish(); void simulate_list_files(); void simulate_check_files(); void simulate_prepare_files(); #endif ioapps-1.4-r2/common.c0000644000175000017500000000150012133726077014220 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ ioapps-1.4-r2/adt/0000755000175000017500000000000012134167012013325 5ustar keruomkeruomioapps-1.4-r2/adt/common.h0000644000175000017500000000307512134052713014774 0ustar keruomkeruom/** * @file common.h * * Common container functions. * * These container macros are shamelessly borrowed from * the linux kernel. * 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CONTAINER_COMMON_H_ #define CONTAINER_COMMON_H_ /** */ #undef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) #else #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif /** Cast a member of a structure out to the containing structure. * The result is NULL if the pointer is NULL. * @param ptr the pointer to the member. * @param type the type of the container struct this is embedded in. * @param member the name of the member within the struct. * */ #define container_of(ptr, type, member) ((ptr) == NULL ? NULL : ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})) #endif ioapps-1.4-r2/adt/list.c0000644000175000017500000001204212134053452014445 0ustar keruomkeruom/** * @file list.c * * Simple doubly linked list implementation. * * Copyright (c) 2001-2010 * Department of Distributed and Dependable Systems * Faculty of Mathematics and Physics * Charles University, Czech Republic * * Licensed under the terms of the GNU General Public License, Version 2 * Signed-off-by: Viliam Holub * Signed-off-by: Lubomir Bulej * Signed-off-by: Petr Tuma * Signed-off-by: Martin Decky * * Modified and enhanced by Jiri Horky * 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "list.h" /** Initialize a list * * @param list The list to initialize. */ void list_init (list_t * list) { list->head = NULL; list->tail = NULL; } /** Initialize an item * * @param item The item to initialize. * */ void item_init (item_t *item) { /* To catch list usage errors, items are initialized. This makes it possible to detect double append and double remove operations easily. */ item->list = NULL; } /** Append an item to a list * * @param list The list to append to. * @param item The item to append. * */ void list_append (list_t *list, item_t *item) { /* Make sure the item is not a member of a list, then add it. */ assert (item->list == NULL); item->list = list; /* In an empty list, attach us as head. Otherwise, attach us to current tail. */ if (list->tail == NULL) { list->head = item; } else { list->tail->next = item; } /* Our previous item is current tail. We obviously have no next item. */ item->prev = list->tail; item->next = NULL; /* We are the new tail. */ list->tail = item; } void list_insert_before (item_t * target, item_t * insert) { assert(target && insert && target->list); insert->list = target->list; insert->prev = target->prev; if (target->prev) { target->prev->next = insert; } else { target->list->head = insert; } insert->next = target; target->prev = insert; } void list_insert_after (item_t * target, item_t * insert) { assert(target && insert && target->list); insert->list = target->list; insert->next = target->next; if (target->next) { target->next->prev = insert; } else { target->list->tail = insert; } insert->prev = target; target->next = insert; } /** Remove an item from the list it currently belongs to. * * @param item The item to remove. * */ void list_remove2(item_t * item) { assert(item->list != NULL); list_remove(item->list, item); } /** Remove an item from a list * * @param list The list to remove from. * @param item The item to remove. * */ void list_remove (list_t *list, item_t *item) { /* Make sure the item is a member of the list, then remove it. */ assert (item->list == list); item->list = NULL; if (item->prev == NULL) /* If we are list head, our next item is the new head. This works even if we happen to be the tail too. */ list->head = item->next; else /* Otherwise, just make our previous item point to our next item. */ item->prev->next = item->next; /* The same for the other end of the list. */ if (item->next == NULL) list->tail = item->prev; else item->next->prev = item->prev; } /** Returns true if the list is empty. * * @param list The list to examine on emptyness. * @return true if the list is empty. */ int list_empty(list_t * list) { return list->head == NULL; } /** Returns the number of elements in the list. * * @param list The list whose length to count. * @return Number of elements in the list. */ ssize_t list_length(list_t * list) { item_t * i; ssize_t length = 0; for (i = list->head; i != NULL; i = i->next) { ++length; } return length; } /** Rotate the list by making its head into its tail * * @param list The list to rotate. * @return The rotated item. * */ item_t *list_rotate (list_t *list) { /* Simply remove and append current list head. Not most efficient but working nonetheless. */ item_t *item = list->head; list_remove (list, item); list_append (list, item); return item; } /** Dumps the @a list * * @arg list list to dump * @arg print_item function to be called to every item */ void list_dump(list_t * list, void (* print_item)(item_t * item)) { item_t * cur = list->head; while (cur) { print_item(cur); fprintf(stderr, " "); cur = cur->next; } fprintf(stderr, "\n"); } ioapps-1.4-r2/adt/fs_trie.c0000644000175000017500000002253712133726077015150 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "fs_trie.h" /** Inserts all necessary nodes that would represent @a full_key in trie @t * @arg t Trie in which to insert * @arg full_key t->delim separated list representing path that should be inserted into @a t * * @return pointer to last created node (or already existent node), or TRIE_ERR in case of error. */ trie_node_t * trie_insert(trie_t * t, const char * full_key) { return trie_insert2(t, full_key, t->new_node); } /** Inserts all necessary nodes that would represent @a full_key in trie @t * @arg t Trie in which to insert * @arg full_key t->delim separated list representing path that should be inserted into @a t * @arg create function to call when creating new nodes * * @return pointer to last created node (or already existent node), or TRIE_ERR in case of error. */ trie_node_t *trie_insert2(trie_t * t, const char * full_key, trie_node_t * (*create)(void)) { int i; trie_node_t * n; trie_node_t * new_node; char delim[2]; char * s; char * strtmp; char * prefix = malloc(sizeof(char) * strlen(full_key) + 1); memset(prefix, 0, strlen(full_key) +1); n = trie_longest_prefix(t, full_key, prefix); if ( ! strcmp(prefix, full_key) ) { //whole key was found free(prefix); return n; } i = 0; if ( n != t->root) { // some prefix was found while (*prefix && prefix[i] == full_key[i]) //skip common part i++; } delim[0] = t->delim; delim[1] = 0; strtmp = strdup(full_key+i); char * saveptr; s = strtok_r(strtmp, delim, &saveptr); while(s) { //Insert all needed nodes //fprintf(stderr, "inserting node with key: %s\n", s); new_node = create(); trie_node_init(new_node); new_node->key = malloc(strlen(s) + 1); new_node->key[0] = 0; strcat(new_node->key, s); list_append(&n->children, &new_node->item); n = new_node; s = strtok_r(NULL, delim, &saveptr); } free(strtmp); free(prefix); return n; } /** Deletes the node @node from trie @t. Note that it also deletes every * (even indirect) child nodes. It doesn't delete t->root node. * @arg t Trie in which to delete * @arg node node to delete * * @return TRIE_OK if just one node was deleted, TRIE_MANY if more than one node was deleted. */ int trie_delete2(trie_t * t, trie_node_t * node) { item_t * i; trie_node_t * n; assert(t); if ( list_length(&node->children) == 0 ) { if (node != t->root) { list_remove2(&node->item); t->delete_node(node); node = NULL; } return TRIE_OK; } else { i = node->children.head; while (i) { n = list_entry(i, trie_node_t, item); i = i->next; trie_delete2(t, n); } //don't forget to delete ourself, if (node != t->root) { list_remove2(&node->item); t->delete_node(node); node = NULL; } return TRIE_MANY; } } /** Destroys whole trie_t tree including root node. * @arg t trie to destroy */ void trie_destroy(trie_t * t) { trie_delete2(t, t->root); t->delete_node(t->root); t->root = NULL; } /** Deletes the node representing @a full_key. Note that it also deletes every * node with keys that has full_key as prefix (every descendent). * @arg t Trie in which to delete * @arg full_key t->delim separated list of keys representing path to the item to delete * * @return TRIE_OK if just one node was deleted, TRIE_MANY if more than one node was deleted, TRIE_ERR if no such * exists in @t. */ int trie_delete(trie_t * t, const char * full_key) { trie_node_t * node; assert(t); node = trie_find(t, full_key); if ( ! node ) { return TRIE_ERR; } else { return trie_delete2(t, node); } } /** Looks for its direct child with given @a part_key * @arg n node in which to look for the child * @arg part_key key that the child should have * * @return pointer to the child or NULL if no such child was found */ trie_node_t * trie_find_child(trie_node_t * n, const char * part_key) { trie_node_t * node = NULL; item_t * i; i = n->children.head; while(i) { node = list_entry(i, trie_node_t, item); if ( strcmp(node->key, part_key) == 0 ) { return node; } i = i->next; } return NULL; } /** Looks for the node that represents @a full_key path * * @arg t trie in which to search * @arg full_key t->delim separated list of keys that should lead to the node * * @return pointer to the node or NULL if no such node was found */ trie_node_t * trie_find(trie_t * t, const char * full_key) { trie_node_t * n; char * s; char * strtmp; assert(t); strtmp = strdup(full_key); char * saveptr; s = strtok_r(strtmp, t->root->key, &saveptr); n = t->root; while(s) { n = trie_find_child(n, s); if ( !n ) { free(strtmp); return NULL; } s = strtok_r(NULL, t->root->key, &saveptr); } free(strtmp); return n; } /** Finds longest existing prefix of @a full_key in trie @a t. It returns * node containg the longest prefix and fills @a buff with the prefix * @arg t Trie in which to search * @arg full_key key to look for * @arg buff buffer of size at least strlen(full_key)+1 that will be filled with the longest prefix found * * @return node containg the longest prefix of @a full_key in the trie @a t. It is, if a root node is returned * no prefix was found. */ trie_node_t * trie_longest_prefix(trie_t * t, const char * full_key, char * buff) { trie_node_t * n1; trie_node_t * n2; const char * c; char * part_key; char lastc; int num_chars = 0; int count = 0; int i = 0; assert(t); buff[0] = 0; part_key = malloc(strlen(full_key) + 1); part_key[0] = 0; n1 = t->root; lastc = 0; c = full_key; while (*c) { if (lastc == t->delim && *c == t->delim ) { //skip more delim characters lastc = *c; c++; count++; } else { if (*c == t->delim && num_chars != 0) { //new delim found and it is not the leading one part_key[i] = 0; n2 = trie_find_child(n1, part_key); if ( !n2 ) { // it didn't find the node free(part_key); return n1; } else { n1 = n2; memcpy(buff, full_key, count * sizeof(char)); buff[count+1] = 0; i = 0; } } if ( *c != t->delim) { part_key[i] = *c; i++; } count++; lastc = *c; c++; num_chars++; } } //the last portion of full_key string wasn't tested (or whole part, if it doesn't contain delimiter) if ( (c = rindex(full_key, t->delim)) == NULL) { // no delimiter at all if ((n2 = trie_find_child(t->root, full_key)) != NULL ) { strcpy(buff, full_key); n1 = n2; } else { n1 = t->root; } } else { //just the last part strcpy(part_key, c+1); n2 = trie_find_child(n1, part_key); if ( n2 ) { // it found the node strcpy(buff, full_key); n1 = n2; } } free(part_key); return n1; } /** Recursively prints the node and all its children. * @arg n node to dump * @arg depth depth in which the node is */ void trie_node_dump(trie_node_t * n, unsigned int depth) { int j; item_t * i; trie_node_t * node; if ( ! n ) { return; } i = n->children.head; for (j = 0; j <= depth; j++) fprintf(stderr, " "); fprintf(stderr, "%s\n", n->key); while(i) { node = list_entry(i, trie_node_t, item); trie_node_dump(node, depth+1); i = i->next; } } /** Dumps trie @a t. * * @arg t trie to dump. */ void trie_dump(trie_t * t) { fprintf(stderr, ".\n"); trie_node_dump(t->root, 0); } /** Returns whether given node is leaf (i.e. has any child) or not. * @arg node to check * @return 1 if the node is leaf, 0 otherwise */ inline int trie_is_leaf(trie_node_t * node) { return node->children.head == NULL; } void trie_apply2(trie_node_t * n, void (* function)(trie_node_t * node)) { item_t * i; trie_node_t * node; if ( ! n ) { return; } i = n->children.head; while(i) { node = list_entry(i, trie_node_t, item); trie_apply2(node, function); i = i->next; } function(n); } void trie_apply(trie_t * t, void (* function)(trie_node_t * node)) { trie_apply2(t->root, function); } void trie_apply2_full(trie_node_t * n, void (* function_full)(trie_node_t * node, char * name), char * name, const char * delim) { item_t * i; trie_node_t * node; char * name_end; if ( ! n ) { return; } name_end = name + strlen(name); if (name[0] != 0 ) { //not empty strcat(name, delim); } strcat(name, n->key); i = n->children.head; while(i) { node = list_entry(i, trie_node_t, item); trie_apply2_full(node, function_full, name, delim); i = i->next; } function_full(n, name); *name_end = 0; } void trie_apply_full(trie_t * t, void (* function_full)(trie_node_t * node, char * full_name)) { char buff[MAX_LINE]; buff[0] = 0; char delim[2]; delim[0] = t->delim; delim[1] = 0; trie_apply2_full(t->root, function_full, buff, delim); } ioapps-1.4-r2/adt/hash_table.c0000644000175000017500000001457511463337051015605 0ustar keruomkeruom/* * Copyright (c) 2006 Jakub Jermar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /******************************************************************************* * This file was shamelessly borrowed from HelenOS (and slightly rewritten) * ******************************************************************************/ /** * @file hash_table.c * @brief Implementation of generic chained hash table. * * This file contains implementation of generic chained hash table. */ #include #include #include #include "../common.h" #include "list.h" #include "hash_table.h" index_t ht_hash_int(hash_table_t * ht, key_t * key) { assert(*key >= 0); //return (*key * 9587 + 13) % ht->entries; return *key % ht->entries; } index_t ht_hash_str(hash_table_t * ht, key_t * key) { char c; char * str = (char *) key; unsigned long index = 4357; while ((c = *str++) != 0) index = c + (index << 6) + (index << 16) - index; return index % ht->entries; } /** Initialize chained hash table and allocate the table of slots (chain holders). * * @param h Hash table structure. Will be initialized by this call. * @param m Number of slots in the hash table. * @param op Hash table operations structure. */ void hash_table_init(hash_table_t *h, ssize_t m, hash_table_operations_t *op) { index_t i; assert(h); assert(op && op->hash && op->compare); h->entry = (list_t *) malloc(m * sizeof(list_t)); if (!h->entry) { DEBUGPRINTF("cannot allocate memory for hash table%s", "\n"); } // memsetb(h->entry, m * sizeof(list_t), 0); for (i = 0; i < m; i++) list_init(&h->entry[i]); h->entries = m; h->op = op; } /** Insert item into hash table. * * @param h Hash table. * @param key Array of all keys necessary to compute hash index. * @param item Item to be inserted into the hash table. */ void hash_table_insert(hash_table_t *h, key_t * key, item_t *item) { index_t chain; assert(item); assert(h && h->op && h->op->hash && h->op->compare); chain = h->op->hash(h, key); assert(chain < h->entries); list_append(&h->entry[chain], item); } /** Search hash table for an item matching key. * * @param h Hash table. * @param key pointer to the key needed to compute hash index. * * @return Matching item on success, NULL if there is no such item. */ item_t *hash_table_find(hash_table_t *h, key_t * key) { item_t *cur; index_t chain; assert(h && h->op && h->op->hash && h->op->compare); chain = h->op->hash(h, key); assert(chain < h->entries); int i =0; for (cur = h->entry[chain].head; cur != NULL; cur = cur->next) { if (h->op->compare(key, cur)) { /* * The entry is there. */ return cur; } i++; } return NULL; } /** Remove all matching items from hash table. * * For each removed item, h->remove_callback() is called. * * @param h Hash table. * @param key pointer to key that will be compared against items of the hash table. */ void hash_table_remove(hash_table_t *h, key_t * key) { index_t chain; item_t *cur; assert(h && h->op && h->op->hash && h->op->compare && h->op->remove_callback); /* * hash_table_find() can be used to find the entry. */ chain = h->op->hash(h, key); cur = hash_table_find(h, key); if (cur) { list_remove(&h->entry[chain], cur); h->op->remove_callback(cur); } return; } /** Prints whole hash table. * * @param h Hash table. * */ void hash_table_dump(hash_table_t * h) { item_t * cur; unsigned int i; assert(h); DEBUGPRINTF("Dumping hash table....%s", "\n"); for (i = 0; i < h->entries; i++) { fprintf(stderr, "[%u]", i); cur = h->entry[i].head; while ( cur != NULL ) { fprintf(stderr, "X"); cur = cur->next; } fprintf(stderr, "\n"); } } /** Applys given function to all item_t. The function should not delete the item! * @param h hash table * @param function function to call on item_t * */ void hash_table_apply(hash_table_t * h, void (* function)(item_t * item)) { item_t * cur; unsigned int i; assert(h); for (i = 0; i < h->entries; i++) { cur = h->entry[i].head; while ( cur != NULL ) { function(cur); cur = cur->next; } } } /** Prints whole hash table, it uses function f to print item_ts * * @param h Hash table. * @param print_item function that takes item_t * as argument and prints it * */ void hash_table_dump2(hash_table_t * h, void (* print_item)(item_t * item)) { item_t * cur; unsigned int i; assert(h); DEBUGPRINTF("Dumping hash table....%s", "\n"); for (i = 0; i < h->entries; i++) { fprintf(stderr, "[%u]", i); cur = h->entry[i].head; while ( cur != NULL ) { print_item(cur); cur = cur->next; } fprintf(stderr, "\n"); } } /** Destroys all content of hash table. The remove callback function * will be called on all items in ht. * * @param h Hash table. * */ void hash_table_destroy(hash_table_t * h) { item_t * cur; item_t * next; unsigned int i; for (i = 0; i < h->entries; i++) { cur = h->entry[i].head; while ( cur != NULL ) { next = cur->next; list_remove(&h->entry[i], cur); h->op->remove_callback(cur); cur = next; } } free(h->entry); } ioapps-1.4-r2/adt/list.h0000644000175000017500000000522212134053452014454 0ustar keruomkeruom/** * @file list.h * * Simple doubly linked list implementation. * * Copyright (c) 2001-2010 * Department of Distributed and Dependable Systems * Faculty of Mathematics and Physics * Charles University, Czech Republic * * Licensed under the terms of the GNU General Public License, Version 2 * Signed-off-by: Viliam Holub * Signed-off-by: Lubomir Bulej * Signed-off-by: Petr Tuma * Signed-off-by: Martin Decky * * Modified and enhanced by Jiri Horky * 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LIST_H_ #define LIST_H_ #include "common.h" #include "../common.h" /* Forward declaration */ struct item; /** A doubly linked list * */ typedef struct list { /** The first item on the list or NULL if empty. */ struct item * head; /** The last item on the list or NULL if empty. */ struct item * tail; } list_t; /** An item of a doubly linked list * * The item should be first in listable structures. * */ typedef struct item { /** The list that we currently belong to. */ struct list * list; /** The next item on the list or NULL if first. */ struct item * prev; /** The previous item on the list or NULL if last. */ struct item * next; } item_t; #define list_entry(ptr, type, member) container_of(ptr, type, member) /* Externals are commented with implementation. */ extern void list_init (list_t * list); extern void item_init (item_t * item); extern void list_insert_before (item_t * target, item_t * insert); extern void list_insert_after (item_t * target, item_t * insert); extern void list_append (list_t * list, item_t * item); extern void list_remove (list_t * list, item_t * item); extern void list_remove2(item_t * item); extern int list_empty (list_t * list); extern ssize_t list_length(list_t * list); extern void list_dump(list_t * list, void (* print_item)(item_t * item)); extern item_t * list_rotate (list_t * list); #endif ioapps-1.4-r2/adt/fs_trie.h0000644000175000017500000000644312133726077015153 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _FS_TRIE_H_ #define _FS_TRIE_H_ #include #include #include #include "list.h" #define TRIE_OK 0 #define TRIE_ALREADY 1 #define TRIE_MANY 2 #define TRIE_ERR -1 /** * Macro for getting a pointer to the structure which contains the trie * structure. * * @param link Pointer to the trie structure. * @param type Name of the outer structure. * @param member Name of trie attribute in the outer structure. */ #define trie_get_instance(node, type, member) \ ((type *)(((uint8_t *)(node)) - ((uint8_t *) &(((type *) NULL)->member)))) typedef struct trie_node trie_node_t; typedef struct trie trie_t; //typedef bool (* trie_walker_t)(trie_node_t *, void *); struct trie_node { list_t children; //list of children item_t item; //I am part of the list of children /** Node's key. */ char * key; }; /** Trie tree structure. */ struct trie { /** Trie root node pointer */ trie_node_t *root; char delim; //create and delete functions for new node trie_node_t *(*new_node)(void); void (*delete_node)(trie_node_t * node); }; /** Initialize node. * * @param node Node which is initialized. */ static inline void trie_node_init(trie_node_t *node) { node->key = NULL; list_init(&node->children); item_init(&node->item); } /** Initialize an empty Trie. * * @arg t trie tree. * @arg delim delimiter to use in trie. */ static inline void trie_init(trie_t *t, char delim, trie_node_t *(* create)(void), void (* del)(trie_node_t * node)) { t->delim = delim; t->new_node = create; t->delete_node = del; t->root = t->new_node(); trie_node_init(t->root); t->root->key = malloc(sizeof(char) * 2); t->root->key[0] = t->delim; t->root->key[1] = 0; } trie_node_t *trie_find(trie_t *t, const char * full_key); trie_node_t * trie_insert(trie_t *t, const char * full_key); trie_node_t * trie_insert2(trie_t * t, const char * full_key, trie_node_t * (*crate)(void)); int trie_delete(trie_t *t, const char * full_key); int trie_delete2(trie_t *t, trie_node_t * node); trie_node_t * trie_find_child(trie_node_t * n, const char * part_key); trie_node_t * trie_longest_prefix(trie_t * t, const char * full_key, char * buff); void trie_destroy(trie_t * t); void trie_dump(trie_t * t); inline int trie_is_leaf(trie_node_t * node); void trie_apply(trie_t * t, void (* function)(trie_node_t * node)); void trie_apply_full(trie_t * t, void (* function_full)(trie_node_t * node, char * name)); //extern void trie_walk(trie_t *t, trie_walker_t walker, void *arg); #endif ioapps-1.4-r2/adt/hash_table.h0000644000175000017500000000714011463337051015600 0ustar keruomkeruom/** * @file * * Implementation of generic chained hash table, * * Copyright (c) 2006 Jakub Jermar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /******************************************************************************** * This file was shamelessly borrowed from HelenOS (and slightly rewritten) * *******************************************************************************/ #ifndef _HASH_TABLE_H_ #define _HASH_TABLE_H_ #include "common.h" #include "../common.h" #include "list.h" typedef ssize_t index_t; #ifndef __key_t_defined typedef ssize_t key_t; #endif struct hash_table; /** Set of operations for hash table. */ typedef struct { /** Hash function. * * @param key Array of keys needed to compute hash index. All keys must * be passed. * * @return Index into hash table. */ index_t (* hash)(struct hash_table * ht, key_t * key); /** Hash table item comparison function. * * @param key Array of keys that will be compared with item. It is not * necessary to pass all keys. * * @return true if the keys match, false otherwise. */ int (*compare)(key_t * key, item_t *item); /** Hash table item removal callback. * * @param item Item that was removed from the hash table. */ void (*remove_callback)(item_t *item); } hash_table_operations_t; /** Hash table structure. */ typedef struct hash_table { list_t *entry; ssize_t entries; hash_table_operations_t *op; } hash_table_t; #define hash_table_entry(item, type, member) \ list_entry((item), type, member) /** common hash fuctions */ index_t ht_hash_int(hash_table_t * ht, key_t * key); index_t ht_hash_str(hash_table_t * ht, key_t * key); extern void hash_table_init(hash_table_t *h, ssize_t m, hash_table_operations_t *op); extern void hash_table_insert(hash_table_t *h, key_t * key, item_t *item); extern item_t *hash_table_find(hash_table_t *h, key_t * key); extern void hash_table_remove(hash_table_t *h, key_t * key); extern void hash_table_dump(hash_table_t * h); extern void hash_table_dump2(hash_table_t * h, void (* print_item)(item_t * item)); extern void hash_table_apply(hash_table_t * h, void (* function)(item_t * item)); extern void hash_table_destroy(hash_table_t *h); extern hash_table_t * hash_table_duplicate(hash_table_t * h); #endif /** @} */ ioapps-1.4-r2/fdmap.c0000644000175000017500000002177212133726077014034 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "fdmap.h" #include "namemap.h" static int ht_compare_fdusage(key_t *key, item_t *item) { fd_usage_t * fd_usage; fd_usage = hash_table_entry(item, fd_usage_t, item); return fd_usage->my_fd == *key; } static int ht_compare_fditem(key_t *key, item_t *item) { fd_item_t * fd_item; fd_item = hash_table_entry(item, fd_item_t, item); return fd_item->old_fd == *key; } static int ht_compare_processhash(key_t *key, item_t *item) { process_hash_item_t * p_item; p_item = hash_table_entry(item, process_hash_item_t, item); return p_item->pid == *key; } static inline void ht_remove_callback_fdusage(item_t * item) { fd_usage_t * fd_usage = hash_table_entry(item, fd_usage_t, item); free(fd_usage); return; } static inline void ht_remove_callback_processhash(item_t * item) { process_hash_item_t * p_item; p_item = hash_table_entry(item, process_hash_item_t, item); free(p_item); return; } inline void ht_remove_callback_fditem(item_t * item) { fd_item_t * fd_item = hash_table_entry(item, fd_item_t, item); free(fd_item); } /** hash table operations. */ static hash_table_operations_t ht_ops_fditem = { .hash = ht_hash_int, .compare = ht_compare_fditem, .remove_callback = ht_remove_callback_fditem /* = NULL if not used */ }; /** hash table operations. */ ///< @todo why is the previous one is static and this can't be using extern? hash_table_operations_t ht_ops_fdusage = { .hash = ht_hash_int, .compare = ht_compare_fdusage, .remove_callback = ht_remove_callback_fdusage /* = NULL if not used */ }; /** hash table operations. */ ///< @todo why is the previous one is static and this can't be using extern? hash_table_operations_t ht_ops_fdmapping = { .hash = ht_hash_int, .compare = ht_compare_processhash, .remove_callback = ht_remove_callback_processhash /* = NULL if not used */ }; /** Frees fd_map structure of fd_item. Useful when deleting whole ht_process_map_t using * remove callback of ht. * @arg item pointer to item_t in fd_item_t structure */ void fd_item_remove_fd_map(item_t * item) { fd_item_t * fd_item = hash_table_entry(item, fd_item_t, item); free(fd_item->fd_map); } /** * Returns hash table of fd mappings for process with pid @a pid, or NULL if such * mapping doesn't exist. * * @arg pid process id to lookup * @arg fd_mappings list of hashtables * @return returns hash table of fd mappings for process with pid @a pid, or NULL if such mapping doesn't exist. */ hash_table_t * get_process_ht(hash_table_t * fd_mappings, int32_t pid) { process_hash_item_t * p_item; item_t * process_ht_item; if ( (process_ht_item = hash_table_find(fd_mappings, &pid)) != NULL ) { p_item = hash_table_entry(process_ht_item, process_hash_item_t, item); if ( pid == p_item->pid) { return (p_item->ht); } } return NULL; } /** Returns pointer to item_t which is part of process_hash_item_t structure. So the * caller can append this item to the list of processes fd mappings * * @arg pid process id for this hash table * @return pointer to item_t which is part of process_hash_item_t structure * */ item_t * new_process_ht(int32_t pid) { process_hash_item_t * p_ht_it = malloc(sizeof(process_hash_item_t)); p_ht_it->ht = malloc(sizeof(hash_table_t)); item_init(&p_ht_it->item); hash_table_init(p_ht_it->ht, HASH_TABLE_SIZE, &ht_ops_fditem); p_ht_it->pid = pid; return &p_ht_it->item; } /** Deletes fd mappings for process with pid @a pid from the list @fd_mappings. * * @arg fd_mappings list of fd mappings (hash tables) * @arg pid process id */ void delete_process_ht(hash_table_t * fd_mappings, int32_t pid) { item_t * process_ht_item; if ( (process_ht_item = hash_table_find(fd_mappings, &pid)) != NULL ) { hash_table_remove(fd_mappings, &pid); } else { ERRORPRINTF("Can not find pid %"PRIi32" when removing delete_process_ht\n", pid); } } /** Duplicates whole hash table of fd mappings * * @parm h Hash table. * @return newly created hash table which is exactl clone of @a h * */ hash_table_t * duplicate_process_ht(hash_table_t * h, hash_table_t * usage_map) { int i; item_t * cur; fd_item_t * fd_it; fd_item_t * fd_it_old; hash_table_t * ht = malloc(sizeof(hash_table_t)); hash_table_init(ht, h->entries, h->op); for (i = 0; i < h->entries; i++) { cur = h->entry[i].head; while ( cur != NULL ) { fd_it = new_fd_item(); fd_it_old = list_entry(cur, fd_item_t, item); fd_it->old_fd = fd_it_old->old_fd; item_init(&fd_it->item); memcpy(fd_it->fd_map, fd_it_old->fd_map, sizeof(fd_map_t)); hash_table_insert(ht, &fd_it->old_fd, &fd_it->item); increase_fd_usage(usage_map, fd_it_old->fd_map->my_fd); cur = cur->next; } } return ht; } void dump_fd_item(fd_item_t * fd_item) { int i; int * parents = fd_item->fd_map->parent_fds; fprintf(stderr, " Old_fd: %d. FD_MAP(%p):my_fd: %d, type: %d: parents:", fd_item->old_fd, fd_item->fd_map, fd_item->fd_map->my_fd, fd_item->fd_map->type); for (i = 0; i < MAX_PARENT_IDS; i++) { fprintf(stderr, "%d ", parents[i]); } fprintf(stderr, "\n"); } void dump_fd_list_item(item_t * it) { fd_item_t * fd_item = list_entry(it, fd_item_t, item); dump_fd_item(fd_item); } void dump_process_hash_list_item(item_t * it) { process_hash_item_t * p_it = hash_table_entry(it, process_hash_item_t, item); fprintf(stderr, " %d", p_it->pid); } inline fd_usage_t * new_fd_usage() { fd_usage_t * fd_usage; fd_usage = malloc(sizeof(fd_usage_t)); item_init(&fd_usage->item); fd_usage->usage = 0; return fd_usage; } inline void delete_fd_usage(fd_usage_t *fd_usage) { free(fd_usage); } inline void increase_fd_usage(hash_table_t * ht, int fd) { item_t * item = hash_table_find(ht, &fd); fd_usage_t * fd_usage; if (item == NULL) { //was not opened before fd_usage = new_fd_usage(); fd_usage->my_fd = fd; fd_usage->usage = 1; hash_table_insert(ht, &fd, &fd_usage->item); } else { //we have it already fd_usage = hash_table_entry(item, fd_usage_t, item); fd_usage->usage++; } } inline int decrease_fd_usage(hash_table_t * ht, int fd) { item_t * item = hash_table_find(ht, &fd); fd_usage_t * fd_usage; if (item == NULL) { //was not opened before ERRORPRINTF("Trying to decrease usage of fd:%d, which doesn't exist!\n", fd); assert(1 == 0); return -1; } else { //we have it already fd_usage = hash_table_entry(item, fd_usage_t, item); fd_usage->usage--; if (fd_usage->usage == 0) { hash_table_remove(ht, &fd); return 1; } } return 0; } inline fd_item_t * new_fd_item() { fd_item_t * fd_item; fd_item = malloc(sizeof(fd_item_t)); item_init(&fd_item->item); fd_item->fd_map = malloc(sizeof(fd_map_t)); memset(fd_item->fd_map->parent_fds, -1, MAX_PARENT_IDS * sizeof(int)); fd_item->fd_map->last_par_index = -1; return fd_item; } inline void delete_fd_item(fd_item_t * item) { free(item->fd_map); item->fd_map = NULL; free(item); item = NULL; } void insert_parent_fd(fd_item_t * fd_item, int fd) { int i; int index = -1; int * parents = fd_item->fd_map->parent_fds; for(i = 0; i < MAX_PARENT_IDS; i++) { if (parents[i] == fd) { ERRORPRINTF("Fd %d is already present in parent fds array...\n", fd); assert(1 == 0); return; } if (parents[i] == -1) { index = i; break; } } if (index != -1) { parents[index] = fd; fd_item->fd_map->last_par_index++; } else { ERRORPRINTF("Array of parrent fds is full! My_fd is: :%d\n", fd_item->fd_map->my_fd); } } /** Deletes fd from list of parents of given fd_item * * @arg bla bla * @return true if this was the last parent of the item, false otherwise */ int delete_parent_fd(fd_item_t * fd_item, int fd) { int i; int * parents = fd_item->fd_map->parent_fds; for(i = 0; i < MAX_PARENT_IDS; i++) { if (parents[i] == fd) { int idx = fd_item->fd_map->last_par_index; if ( idx == 0 ) { parents[i] = -1; } else if ( idx > 0 ) { parents[i] = parents[idx]; } else { ERRORPRINTF("Sanity check error: last_par_index out of bounds: %d\n", idx); assert(1 == 0); } fd_item->fd_map->last_par_index--; break; } if (parents[i] == -1) { ERRORPRINTF("Didn't find fd %d in parent fds\n", fd); assert(1 == 0); break; } } return fd_item->fd_map->last_par_index == -1; } ioapps-1.4-r2/ioprofiler-trace0000755000175000017500000000313612127356207015764 0ustar keruomkeruom#!/bin/bash DEFAULT_OUTFILE=ioproftrace.log function help() { echo "Simple wrapper around strace call. Part of IOapps suite." >&2 echo >&2 echo "Save strace output to (defaults to $DEFAULT_OUTFILE" >&2 echo "and also adds .log to the name if not specified)" >&2 echo "If -b is specified, ioreplay gets called to convert the trace to" >&2 echo "binary form and .bin is appended." >&2 echo "Either file can be opened by ioprofiler to visualize the io profile" >&2 echo "If you want to know more, read this script and/or for more info on ioapps," >&2 echo "see: http://code.google.com/p/ioapps/" >&2 echo >&2 echo "Usage:">&2 echo " $(basename $1) [-o out_file] [-b] program [args]">&2 echo "or for tracing already running programs">&2 echo " $(basename $1) [-o out_file] [-b] -p pid">&2 } while getopts ":bho:p:" opt; do case $opt in b) TOBIN=yes ;; o) OUTFILE=$OPTARG ;; p) PID=$OPTARG ;; h) help $0 exit 0 ;; \?) echo "Invalid option: -$OPTARG" >&2 echo help $0 exit 1 ;; esac done shift $((OPTIND-1)) OUTFILE=${OUTFILE:-$DEFAULT_OUTFILE} OUTFILE=${OUTFILE%.log}.log if [ -z $PID ]; then if [ $# -eq 0 ]; then echo "Not enough parameters." echo help $0 exit 1 fi strace -q -a1 -s0 -f -tttT -o${OUTFILE} -e trace=file,desc,process,socket "$@" else strace -q -a1 -s0 -f -tttT -o${OUTFILE} -e trace=file,desc,process,socket -p $PID fi if [ -n "$TOBIN" ]; then BIN_OUTFILE=${OUTFILE%.log}.bin echo running ioreplay to convert ${OUTFILE} to ${BIN_OUTFILE} >&2 ioreplay -c -f ${OUTFILE} -o ${BIN_OUTFILE} fi ioapps-1.4-r2/ioreplay.c0000644000175000017500000002520412133726077014563 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "common.h" #include "ioreplay.h" #include "print.h" #include "replicate.h" #include "stats.h" #include "simulate.h" #include "in_strace.h" #include "in_binary.h" #define FORMAT_STRACE "strace" #define FORMAT_BIN "bin" static struct option ioreplay_options[] = { /* name has_arg flag value */ { "bind", 1, NULL, 'b' }, { "convert", 0, NULL, 'c' }, { "check", 0, NULL, 'C' }, { "dont-fix", 0, NULL, 'd' }, { "file", 1, NULL, 'f' }, { "format", 1, NULL, 'F' }, { "help", 0, NULL, 'h' }, { "ignore", 1, NULL, 'i' }, { "map", 0, NULL, 'm' }, { "output", 1, NULL, 'o' }, { "replicate", 0, NULL, 'r' }, { "prepare", 0, NULL, 'p' }, { "print", 0, NULL, 'P' }, { "scale", 1, NULL, 's' }, { "stats", 1, NULL, 'S' }, { "timing", 1, NULL, 't' }, { "verbose", 0, NULL, 'v' }, { "version", 0, NULL, 'V' }, { NULL, 0, NULL, 0 } }; struct timeval global_start; void help(char * name) { printf("Replicates all IO syscalls defined in file by -f option.\n\n"); printf("Usage: %s [OPERATION] -f [OPTIONS]\n\n", name); printf("%s is primary used to replicate recorded IO system calls.\n\ In order to do that, several other helper functionality exists.\n\n", name); printf("Usage: %s -c -f [-F ] [-o ] [-v]\n", name); printf(" converts in format to binary form into file \n\n"); printf("Usage: %s -S -f [-v]\n", name); printf(" displays some statistics about syscalls recorded in (must be in " FORMAT_STRACE " format)\n\n"); printf("Usage: %s -P -f [-F ] [-v]\n", name); printf(" prints syscalls in normalized format\n\n"); printf("Usage: %s -C -f [-F ] [-i ] [-m ] [-v]\n", name); printf(" checks whether local enviroment is ready for replaying traces recorded in .\n\n"); printf("Usage: %s -r -f [-F ] [-t ] [-s ] [-b ] [-i ] [-m ] [-v]\n", name); printf(" replicates traces recorded in . Use -C prior to running this.\n"); printf("\n\ -b --bind bind replicating process to processor number \n\ -c --convert file to binary form, see also -o\n\ -C --check checks that all operations recorded in the file specied by -f will\n\ succeed (ie. will result in same return code).\n\ It takes -i and -m into account. See also -p.\n\ -d --dont-fix turns off fixing of missing system calls (uncomplete strace output support)\n\ -f --file sets filename to \n\ -F --format specifies input format of the file.\n\ Options: " FORMAT_STRACE ", " FORMAT_BIN ".\n\ Check README for details. Default is " FORMAT_STRACE ".\n\ -h --help prints this message\n\ -i --ignore sets file containing names which we should not touch during\n\ replaying. I.e. no syscall operation will be performed on given file.\n\ -m --map sets containing file names mapping. When opening file,\n\ if there is mapping for it, it will open mapped file instead.\n\ See README for more information.\n\ -o --output output filename when converting. Default: strace.bin.\n\ -p --prepare will prepare all files accesses recorded in file specified by -f,\n\ so every IO operation will return with same exit code as in original\n\ application. See also -i and/or -m parameters.\n\ Do nothing at the moment.\n\ -P --print prints recorded syscalls in normalized format regardless the format\n\ in which are the syscalls stored now\n\ -r --replicate will replicate every operation stored in file specified by -f\n\ -s --scale scales delays between calls by the factor . Used with -r.\n\ -S --stats generate stats when processing the file. Can be combined with other\n\ options.\n\ -t --timing sets timing mode for replication. Options available:\n\ diff - default mode. makes sure that gaps between calls are the same as in the original run.\n\ asap - makes calls one just after another.\n\ exact - makes sure that calls are (approximately) done in the same time as in the original run\n\ (relative from start of the application)\n\ -v --verbose be more verbose (do nothing at the moment)\n\ -V --version prints version and exits.\n"); } void print_version() { printf("ioreplay v%s, part of IOapps\n\ \n\ Copyright (C) 2010 Jiri Horky \n\ \n\ License GPLv2: GNU GPL version 2 \n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ \n\ Written by Jiri Horky, 2010-2013\n", VERSION); } int main(int argc, char * * argv) { char format[MAX_STRING] = FORMAT_STRACE; char filename[MAX_STRING] = "strace.file"; char output[MAX_STRING] = "strace.bin"; char ignorefile[MAX_STRING] = ""; char mapfile[MAX_STRING] = ""; list_t * list; int len = 0; int retval; int action = FIX_MISSING; int verbose = 0; char c; int cpu; double scale = 1.0; #ifdef _SC_NPROCESSORS_ONLN //we can determine how many processors we have srandom((int)time(NULL)); cpu = random() % sysconf(_SC_NPROCESSORS_ONLN); #else cpu = 0; #endif gettimeofday(&global_start, NULL); /* Parse parameters */ while ((c = getopt_long (argc, argv, "b:cCdf:F:hi:m:Mo:pPrs:St:vV", ioreplay_options, NULL)) != -1 ) { switch (c) { case 'b': cpu = atoi(optarg); break; case 'c': action |= ACT_CONVERT; break; case 'C': action |= ACT_CHECK; break; case 'd': action &= ~FIX_MISSING; //turn off fixing missing calls feature fprintf(stderr, "Turning off fix_missing...\n"); break; case 'f': strncpy(filename, optarg, MAX_STRING); break; case 'F': strncpy(format, optarg, MAX_STRING); break; case 'h': help(basename(argv[0])); return 0; break; case 'i': strncpy(ignorefile, optarg, MAX_STRING); break; case 'm': strncpy(mapfile, optarg, MAX_STRING); break; case 'M': action |= ACT_SIMULATE; break; case 'o': strncpy(output, optarg, MAX_STRING); break; case 'r': action |= ACT_REPLICATE; break; case 'p': action |= ACT_PREPARE; break; case 'P': action |= ACT_PRINT; break; case 's': scale = atof(optarg); if (scale == 0) { fprintf(stderr, "Error parsing scale parameter\n"); exit(-1); } break; case 'S': action |= ACT_STATS; break; case 't': if ( ! strcmp(TIME_DIFF_STR, optarg) ) { action = (action & ~TIME_MASK) | TIME_DIFF; } else if ( ! strcmp(TIME_EXACT_STR, optarg) ) { action = (action & ~TIME_MASK) | TIME_EXACT; } else if ( ! strcmp(TIME_ASAP_STR, optarg) ) { action = (action & ~TIME_MASK) | TIME_ASAP; } else { fprintf(stderr, "Unknown timemode specified.\n"); exit(-1); } break; case 'v': verbose = 1; break; case 'V': print_version(); return 0; break; default: fprintf(stderr, "Unknown parameter: %s\n", argv[optind-1]); return -1; break; } } list = (list_t *)malloc(sizeof(list_t)); list_init(list); if ( !strcmp(format, FORMAT_STRACE)) { if ( (retval = strace_get_items(filename, list, action & ACT_STATS)) != 0) { DEBUGPRINTF("Error parsing file %s, exiting\n", filename); return retval; } } else if ( !strcmp(format, FORMAT_BIN)) { if ( (retval = bin_get_items(filename, list)) != 0) { DEBUGPRINTF("Error parsing file %s, exiting\n", filename); return retval; } } else { ERRORPRINTF("Unknown format identifier: %s\n", format); return -1; } len = list_length(list); DEBUGPRINTF("Loading done, %d items loaded.\n", len); char * ifilename = ignorefile; if (strlen(ignorefile) == 0) { ifilename = NULL; } char * mfilename = mapfile; if (strlen(mapfile) == 0) { mfilename = NULL; } if ( len == 0 ) { fprintf(stdout, "No items loaded, nothin to do --> exiting.\n"); return 0; } if (action & ACT_PRINT) { DEBUGPRINTF("Listing all syscalls in normalized format...%s", "\n"); print_items(list); } else if (action & ACT_CONVERT) { DEBUGPRINTF("Saving in binary form...%s", "\n"); bin_save_items(output, list); } else if (action & ACT_SIMULATE) { simulate_init(ACT_SIMULATE); if (replicate(list, cpu, scale, action, ifilename, mfilename) != 0) { ERRORPRINTF("An error occurred during replicating.%s", "\n"); } simulate_finish(); } else if (action & ACT_CHECK) { simulate_init(ACT_CHECK); if (replicate(list, cpu, scale, action | ACT_SIMULATE, ifilename, mfilename) != 0) { ERRORPRINTF("An error occurred during replicating.%s", "\n"); } simulate_check_files(); simulate_finish(); } else if (action & ACT_PREPARE) { simulate_init(ACT_PREPARE); if (replicate(list, cpu, scale, action, ifilename, mfilename) != 0) { ERRORPRINTF("An error occurred during replicating.%s", "\n"); } ///< @todo to change //simulate_prepare_files(); simulate_finish(); } else if (action & ACT_REPLICATE) { DEBUGPRINTF("Starting of replicating...%s", "\n"); if ( ! (action & TIME_MASK) ) { //time mode not defined action |= TIME_DIFF; //use time diff as default } /// < @todo to change if (replicate(list, cpu, scale, action, ifilename, mfilename) != 0) { ERRORPRINTF("An error occurred during replicating.%s", "\n"); } } else { ERRORPRINTF("No action specified!%s", "\n"); } remove_items(list); free(list); return 0; } ioapps-1.4-r2/simulate.c0000644000175000017500000003416612133726077014571 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "simulate.h" #include "simfs.h" hash_table_t * sim_map_read = NULL; hash_table_t * sim_map_write = NULL; list_t * sim_list_special = NULL; int sim_mode = 0; static int ht_compare_sim(key_t *key, item_t *item) { sim_item_t * sim_item; sim_item = hash_table_entry(item, sim_item_t, item); return ! strncmp(sim_item->name, (char *) key, MAX_STRING); } static inline void ht_remove_callback_sim(item_t * item) { sim_item_t * sim_item = hash_table_entry(item, sim_item_t, item); item_t * i; rw_op_t * rw_op; //clear list first: i = sim_item->list.head; while(i) { rw_op = list_entry(i, rw_op_t, item); i = i->next; free(rw_op); } free(sim_item); return; } /** hash table operations. */ static hash_table_operations_t ht_ops_sim = { .hash = ht_hash_str, .compare = ht_compare_sim, .remove_callback = ht_remove_callback_sim /* = NULL if not used */ }; /** Inits all structures needed for simulation. You have to call this function before calling any * of other simulate_* functions. * @arg mode mode in which to simulateing. Possible options are: ACT_PREPARE, ACT_CHECK and ACT_SIMULATE. * ACT_SIMULATE: Only processes read and writes and make sim_map_read/write hash maps. * ACT_CHECK: Also checks whether file structure on the disks is ok or not. * ACT_PREPARE: Also tries to fix the filesystem. */ void simulate_init(int mode) { if (sim_map_read != NULL) { ERRORPRINTF("It seems the sim_map_read hash table is already initialized!%s", "\n"); return; } if (sim_map_write != NULL) { ERRORPRINTF("It seems the sim_map_read hash table is already initialized!%s", "\n"); return; } sim_mode = mode; sim_map_read = malloc(sizeof(hash_table_t)); sim_map_write = malloc(sizeof(hash_table_t)); hash_table_init(sim_map_read, HASH_TABLE_SIZE, &ht_ops_sim); hash_table_init(sim_map_write, HASH_TABLE_SIZE, &ht_ops_sim); simfs_init(); } /** Frees all variables used for simulations. Call this function at the end, as you will not be able * to get any simulation information after calling it. */ void simulate_finish() { if (sim_map_read == NULL) { ERRORPRINTF("Sim_map_read already freed. Double finish?%s", "\n"); return; } if (sim_map_write == NULL) { ERRORPRINTF("Sim_map_write already freed. Double finish?%s", "\n"); return; } hash_table_destroy(sim_map_read); hash_table_destroy(sim_map_write); free(sim_map_read); free(sim_map_write); sim_map_read = NULL; sim_map_write = NULL; DEBUGPRINTF("going to finish simfs%s","\n"); simfs_finish(); } inline sim_item_t * simulate_get_sim_item(fd_item_t * fd_item, hash_table_t * ht) { item_t * item; sim_item_t * sim_item; if ( (item = hash_table_find(ht, (key_t *)(fd_item->fd_map->name))) == NULL) { sim_item = malloc(sizeof(sim_item_t)); sim_item->time_open = fd_item->fd_map->time_open; sim_item->created = fd_item->fd_map->created; strncpy(sim_item->name, fd_item->fd_map->name, MAX_STRING); list_init(&sim_item->list); item_init(&sim_item->item); hash_table_insert(ht, (key_t *) sim_item->name, &sim_item->item); } else { sim_item = hash_table_entry(item, sim_item_t, item); } return sim_item; } inline void simulate_append_rw(sim_item_t * sim_item, int64_t size, int64_t offset, struct int32timeval start, int32_t dur, int64_t retval) { rw_op_t * rw_op = malloc(sizeof(rw_op_t)); item_init(&rw_op->item); rw_op->size = retval; //we notice only how many bytes were actually read/written, not tried rw_op->offset = offset; rw_op->start = start; rw_op->dur = dur; list_append(&sim_item->list, &rw_op->item); } inline void simulate_write(fd_item_t * fd_item, write_item_t * op_it) { simfs_t * simfs = simfs_find(fd_item->fd_map->name); sim_item_t * sim_item = NULL; uint64_t off; if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { if ( ! simfs) { DEBUGPRINTF("Entry for %s in simfs missing, which might be OK (e.g. tmp file created,unlinked,written and then closed)\n", fd_item->fd_map->name); return; } //offset is changed in replicate functions, this is to be changed. off = fd_item->fd_map->cur_pos + op_it->o.retval; if (simfs->virt_size < off) { simfs->virt_size = off; } if (simfs->physical) { if ( fd_item->fd_map->cur_pos > simfs->phys_size ) { ERRORPRINTF("Write to file %s on pos %"PRIu64" would fail as the current position is behind end of the file(%"PRIu64").\n", fd_item->fd_map->name, fd_item->fd_map->cur_pos, simfs->phys_size); } else { if (simfs->phys_size < off) { simfs->phys_size = off; } } } } if ( sim_mode & ACT_SIMULATE) { sim_item = simulate_get_sim_item(fd_item, sim_map_write); simulate_append_rw(sim_item, op_it->o.size, fd_item->fd_map->cur_pos, op_it->o.info.start, op_it->o.info.dur, op_it->o.retval); } } inline void simulate_read(fd_item_t * fd_item, read_item_t * op_it) { simfs_t * simfs = simfs_find(fd_item->fd_map->name); sim_item_t * sim_item = NULL; uint64_t off; if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { if ( ! simfs) { DEBUGPRINTF("Entry for %s in simfs missing, which might be OK (e.g. tmp file created/unlinked and then written)\n", fd_item->fd_map->name); return; } off = fd_item->fd_map->cur_pos + op_it->o.retval; if (simfs->virt_size < off) { simfs->virt_size = off; } } if (sim_mode & ACT_SIMULATE) { sim_item = simulate_get_sim_item(fd_item, sim_map_read); simulate_append_rw(sim_item, op_it->o.size, fd_item->fd_map->cur_pos, op_it->o.info.start, op_it->o.info.dur, op_it->o.retval); } } inline void simulate_sendfile(fd_item_t * in_fd_item, fd_item_t * out_fd_item, sendfile_item_t * op_it) { simfs_t * in_simfs; simfs_t * out_simfs; sim_item_t * sim_item_read = NULL; sim_item_t * sim_item_write = NULL; uint64_t off; out_simfs = simfs_find(out_fd_item->fd_map->name); if (in_fd_item) { in_simfs = simfs_find(in_fd_item->fd_map->name); if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { if ( ! in_simfs) { DEBUGPRINTF("Entry for %s in simfs missing, which might be OK (e.g. tmp file created/unlinked and then written)\n", in_fd_item->fd_map->name); return; } if ( op_it->o.offset == OFFSET_INVAL) { off = in_fd_item->fd_map->cur_pos + op_it->o.retval; } else { off = op_it->o.offset + op_it->o.retval; } if (in_simfs->virt_size < off) { in_simfs->virt_size = off; } } if (sim_mode & ACT_SIMULATE) { sim_item_read = simulate_get_sim_item(in_fd_item, sim_map_read); if ( op_it->o.offset == OFFSET_INVAL) { off = in_fd_item->fd_map->cur_pos; } else { off = op_it->o.offset; } simulate_append_rw(sim_item_read, op_it->o.size, off, op_it->o.info.start, op_it->o.info.dur, op_it->o.retval); } } if (out_fd_item) { out_simfs = simfs_find(out_fd_item->fd_map->name); if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { if ( ! out_simfs) { DEBUGPRINTF("Entry for %s in simfs missing, which might be OK (e.g. tmp file created/unlinked and then written)\n", out_fd_item->fd_map->name); return; } off = out_fd_item->fd_map->cur_pos + op_it->o.retval; if (out_simfs->virt_size < off) { out_simfs->virt_size = off; } } if (sim_mode & ACT_SIMULATE) { sim_item_write = simulate_get_sim_item(out_fd_item, sim_map_write); off = out_fd_item->fd_map->cur_pos; simulate_append_rw(sim_item_write, op_it->o.size, off, op_it->o.info.start, op_it->o.info.dur, op_it->o.retval); } } } inline void simulate_pwrite(fd_item_t * fd_item, pwrite_item_t * op_it) { simfs_t * simfs = simfs_find(fd_item->fd_map->name); sim_item_t * sim_item = NULL; uint64_t off; if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { if ( ! simfs) { DEBUGPRINTF("Entry for %s in simfs missing, which might be OK (e.g. tmp file created,unlinked,written and then closed)\n", fd_item->fd_map->name); return; } off = fd_item->fd_map->cur_pos; if (simfs->virt_size < off) { simfs->virt_size = off; } if (simfs->physical) { if ( fd_item->fd_map->cur_pos > simfs->phys_size ) { ERRORPRINTF("Pwrite to file %s on pos %"PRIu64" would fail as the current position is behind end of the file(%"PRIu64").\n", fd_item->fd_map->name, fd_item->fd_map->cur_pos, simfs->phys_size); } else { if (simfs->phys_size < off) { simfs->phys_size = off; } } } } if ( sim_mode & ACT_SIMULATE) { sim_item = simulate_get_sim_item(fd_item, sim_map_write); simulate_append_rw(sim_item, op_it->o.size, op_it->o.offset, op_it->o.info.start, op_it->o.info.dur, op_it->o.retval); } } inline void simulate_pread(fd_item_t * fd_item, pread_item_t * op_it) { simfs_t * simfs = simfs_find(fd_item->fd_map->name); sim_item_t * sim_item = NULL; uint64_t off; if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { if ( ! simfs) { DEBUGPRINTF("Entry for %s in simfs missing, which might be OK (e.g. tmp file created/unlinked and then written)\n", fd_item->fd_map->name); return; } off = fd_item->fd_map->cur_pos; if (simfs->virt_size < off) { simfs->virt_size = off; } } if (sim_mode & ACT_SIMULATE) { sim_item = simulate_get_sim_item(fd_item, sim_map_read); simulate_append_rw(sim_item, op_it->o.size, op_it->o.offset, op_it->o.info.start, op_it->o.info.dur, op_it->o.retval); } } void simulate_access(access_op_t * op_it) { if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { simfs_access(op_it); } } void simulate_stat(stat_op_t * op_it) { if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { simfs_stat(op_it); } } void simulate_mkdir(mkdir_op_t * op_it) { if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { simfs_mkdir(op_it); } } void simulate_rmdir(rmdir_op_t * op_it) { if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { simfs_rmdir(op_it); } } void simulate_unlink(unlink_op_t * op_it) { if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { simfs_unlink(op_it); } } void simulate_creat(open_op_t * op_it) { if (sim_mode & ACT_CHECK || sim_mode & ACT_PREPARE) { simfs_creat(op_it); } } hash_table_t * simulate_get_map_read() { return sim_map_read; } hash_table_t * simulate_get_map_write() { return sim_map_write; } inline int simulate_get_open_fd() { static int fd = 1000-1; fd++; return fd; } inline int64_t simulate_get_max_offset(sim_item_t * sim_item) { rw_op_t * rw_op; uint64_t max_off = 0; uint64_t off = 0; item_t * i; i = sim_item->list.head; while (i) { rw_op = list_entry(i, rw_op_t, item); off = rw_op->offset + rw_op->size; if ( off > max_off ) { max_off = off; } i = i->next; } return max_off; } /** Prints filename and its size according to the successful read operations. * @arg item item pointer in sim_item_t */ void simulate_print_filename_size(item_t * item) { uint64_t max_off = 0; sim_item_t * sim_item = hash_table_entry(item, sim_item_t, item); max_off = simulate_get_max_offset(sim_item); printf("%s: ", sim_item->name); printf("%"PRIu64"B\n", max_off); } /** Checks given file for existence, read permission and if it has enough size. It ignores files that * was created by the application itself. * @arg item item pointer in sim_item_t */ void simulate_check_file_read(item_t * item) { struct stat st; uint64_t max_off = 0; sim_item_t * sim_item = hash_table_entry(item, sim_item_t, item); if ( sim_item->created ) { //the file was created, don't control this one return; } max_off = simulate_get_max_offset(sim_item); if ( access(sim_item->name, R_OK) != 0 ) { ERRORPRINTF("%s: Can't open: %s\n", sim_item->name, strerror(errno)); return; } if (simfs_has_file(sim_item->name) == SIMFS_PHYS) { stat(sim_item->name, &st); if (st.st_size < max_off) { ERRORPRINTF("%s: Too small (%"PRIu64"), expected: %"PRIu64" bytes\n", sim_item->name, st.st_size, max_off); } } else { } stat(sim_item->name, &st); if (st.st_size < max_off) { ERRORPRINTF("%s: Too small (%"PRIu64"), expected: %"PRIu64" bytes\n", sim_item->name, st.st_size, max_off); } } /** Checks whether a) given file exists and is writable or b) path for it exists and destination * directory is writable and has enough space. All mkdir calls are taken into account. * @arg item item pointer in sim_item_t */ void simulate_check_file_write(item_t * item) { } /** Prints all files accessed with its size (if applicable) as seen from IO operations. */ void simulate_list_files() { printf("Read files:\n"); hash_table_apply(sim_map_read, simulate_print_filename_size); printf("\nWrite files:\n"); hash_table_apply(sim_map_write, simulate_print_filename_size); } void simulate_check_file(simfs_t * simfs, char * full_name) { if ( simfs->physical) { if ( simfs->virt_size > simfs->phys_size) { fprintf(stderr, "%s %"PRIu64": File is too small, recreate it.\n", full_name, simfs->virt_size); } } else { //virtual if ( simfs_is_file(simfs) && simfs->created == 0) { fprintf(stderr, "%s %"PRIu64": File doesn't exist at all.\n", full_name, simfs->virt_size ); } } /** * @todo Think about this. Can I delete this comment? BE CAREFULL HERE. WHAT ABOUT READS TO THE FILES THAT ARE UNLINKED BEFORE CLOSE?! */ } /** Checks for existence, size and read/write permission of all files * that would be accessed during replication of IO operations. Outputs error for every problematic file. */ void simulate_check_files() { fprintf(stderr, "Create following files:\n"); simfs_apply_full_name(simulate_check_file); } ioapps-1.4-r2/in_common.h0000644000175000017500000001126112133726077014720 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _REPIO_COMMON_H_ #define _REPIO_COMMON_H_ #include #include #include #include #include "common.h" #define MODE_UNDEF -666 /** The following structure is used to allow insertion of several different types of events/operations into * one list. Given a list, one then need to recognize the type of the the structure. If we keep the same offset * from the begging of the structure of "item_t item" and "char type", one can retrieve the information using * this common item, and then, according to the type, retrieves the true structure. * * It IS somehow nasty, but other solutions (having two lists, write type information to the item_t itself) seem * to me worse. */ typedef struct common_op_item { item_t item; /* make me item of the list - it MUST be defined first */ char type; } common_op_item_t; /* These are to cover all structures with item_t, so they can be member of the list. */ typedef struct read_item { item_t item; /* make me item of the list - it MUST be defined first */ char type; /* and this MUST be defined as second */ read_op_t o; } read_item_t; typedef struct write_item { item_t item; /* make me item of the list - it MUST be defined first */ char type; /* and this MUST be defined as second */ write_op_t o; } write_item_t; typedef struct pread_item { item_t item; /* make me item of the list - it MUST be defined first */ char type; /* and this MUST be defined as second */ pread_op_t o; } pread_item_t; typedef struct pwrite_item { item_t item; /* make me item of the list - it MUST be defined first */ char type; /* and this MUST be defined as second */ pwrite_op_t o; } pwrite_item_t; typedef struct pipe_item { item_t item; char type; pipe_op_t o; } pipe_item_t; typedef struct mkdir_item { item_t item; char type; mkdir_op_t o; } mkdir_item_t; typedef struct rmdir_item { item_t item; char type; rmdir_op_t o; } rmdir_item_t; typedef struct clone_item { item_t item; char type; clone_op_t o; } clone_item_t; typedef struct dup_item { item_t item; char type; dup_op_t o; } dup_item_t; typedef struct open_item { item_t item; char type; open_op_t o; } open_item_t; typedef struct close_item { item_t item; char type; close_op_t o; } close_item_t; typedef struct unlink_item { item_t item; char type; unlink_op_t o; } unlink_item_t; typedef struct llseek_item { item_t item; char type; llseek_op_t o; } llseek_item_t; typedef struct lseek_item { item_t item; char type; lseek_op_t o; } lseek_item_t; typedef struct access_item { item_t item; char type; access_op_t o; } access_item_t; typedef struct stat_item { item_t item; char type; stat_op_t o; } stat_item_t; typedef struct socket_item { item_t item; char type; socket_op_t o; } socket_item_t; typedef struct sendfile_item { item_t item; char type; sendfile_op_t o; } sendfile_item_t; /* Functions for creating new structures */ write_item_t * new_write_item(); read_item_t * new_read_item(); pwrite_item_t * new_pwrite_item(); pread_item_t * new_pread_item(); mkdir_item_t * new_mkdir_item(); rmdir_item_t * new_rmdir_item(); dup_item_t * new_dup_item(); clone_item_t * new_clone_item(); pipe_item_t * new_pipe_item(); open_item_t * new_open_item(); close_item_t * new_close_item(); unlink_item_t * new_unlink_item(); llseek_item_t * new_llseek_item(); lseek_item_t * new_lseek_item(); access_item_t * new_access_item(); stat_item_t * new_stat_item(); socket_item_t * new_socket_item(); sendfile_item_t * new_sendfile_item(); int remove_items(list_t * list); int strccount(char * str, char c); int read_integer(char * str, long long * num); int read_clone_flags(char * flags); int read_open_flags(char * flags); int read_seek_flag(char * flag); int read_access_flags(char * str); int read_dup3_flags(char * str); struct int32timeval read_time(char * timestr); int32_t read_duration(char * timestr); #endif ioapps-1.4-r2/in_common.c0000644000175000017500000002653012133726077014720 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "in_common.h" write_item_t * new_write_item() { write_item_t * i; i = malloc(sizeof(write_item_t)); item_init(&i->item); return i; } pwrite_item_t * new_pwrite_item() { pwrite_item_t * i; i = malloc(sizeof(pwrite_item_t)); item_init(&i->item); return i; } read_item_t * new_read_item() { read_item_t * i; i = malloc(sizeof(read_item_t)); item_init(&i->item); return i; } pread_item_t * new_pread_item() { pread_item_t * i; i = malloc(sizeof(pread_item_t)); item_init(&i->item); return i; } mkdir_item_t * new_mkdir_item() { mkdir_item_t * i; i = malloc(sizeof(mkdir_item_t)); item_init(&i->item); return i; } rmdir_item_t * new_rmdir_item() { rmdir_item_t * i; i = malloc(sizeof(rmdir_item_t)); item_init(&i->item); return i; } dup_item_t * new_dup_item() { dup_item_t * i; i = malloc(sizeof(dup_item_t)); item_init(&i->item); #ifndef NDEBUG memset(i, 0, sizeof(*i)); #endif return i; } clone_item_t * new_clone_item() { clone_item_t * i; i = malloc(sizeof(clone_item_t)); item_init(&i->item); return i; } pipe_item_t * new_pipe_item() { pipe_item_t * i; i = malloc(sizeof(pipe_item_t)); #ifndef NDEBUG memset(i, 0, sizeof(*i)); #endif item_init(&i->item); return i; } open_item_t * new_open_item() { open_item_t * i; i = malloc(sizeof(open_item_t)); item_init(&i->item); return i; } close_item_t * new_close_item() { close_item_t * i; i = malloc(sizeof(close_item_t)); item_init(&i->item); return i; } unlink_item_t * new_unlink_item() { unlink_item_t * i; i = malloc(sizeof(unlink_item_t)); item_init(&i->item); return i; } llseek_item_t * new_llseek_item() { llseek_item_t * i; i = malloc(sizeof(llseek_item_t)); item_init(&i->item); return i; } lseek_item_t * new_lseek_item() { lseek_item_t * i; i = malloc(sizeof(lseek_item_t)); item_init(&i->item); return i; } access_item_t * new_access_item() { access_item_t * i; i = malloc(sizeof(access_item_t)); item_init(&i->item); return i; } stat_item_t * new_stat_item() { stat_item_t * i; i = malloc(sizeof(stat_item_t)); item_init(&i->item); return i; } socket_item_t * new_socket_item() { socket_item_t * i; i = malloc(sizeof(socket_item_t)); item_init(&i->item); return i; } sendfile_item_t * new_sendfile_item() { sendfile_item_t * i; i = malloc(sizeof(sendfile_item_t)); item_init(&i->item); return i; } /** Removes and unallocates lists of syscalls. * * @arg list list of syscalls to delete * @return zero if ok, non-zero otherwise */ int remove_items(list_t * list) { long long i = 0; item_t * item = list->head; common_op_item_t * com_it; read_item_t * read_it; write_item_t * write_it; pread_item_t * pread_it; pwrite_item_t * pwrite_it; open_item_t * open_it; close_item_t * close_it; unlink_item_t * unlink_it; lseek_item_t * lseek_it; llseek_item_t * llseek_it; clone_item_t * clone_it; dup_item_t * dup_it; mkdir_item_t * mkdir_it; rmdir_item_t * rmdir_it; pipe_item_t * pipe_it; access_item_t * access_it; stat_item_t * stat_it; socket_item_t * socket_it; sendfile_item_t * sendfile_it; while (item) { i++; com_it = list_entry(item, common_op_item_t, item); switch (com_it->type) { case OP_WRITE: write_it = (write_item_t *) com_it; item = write_it->item.next; free(write_it); break; case OP_READ: read_it = (read_item_t *) com_it; item = read_it->item.next; free(read_it); break; case OP_PWRITE: pwrite_it = (pwrite_item_t *) com_it; item = pwrite_it->item.next; free(pwrite_it); break; case OP_PREAD: pread_it = (pread_item_t *) com_it; item = pread_it->item.next; free(pread_it); break; case OP_OPEN: open_it = (open_item_t *) com_it; item = open_it->item.next; free(open_it); break; case OP_CLOSE: close_it = (close_item_t *) com_it; item = close_it->item.next; free(close_it); break; case OP_UNLINK: unlink_it = (unlink_item_t *) com_it; item = unlink_it->item.next; free(unlink_it); break; case OP_LSEEK: lseek_it = (lseek_item_t *) com_it; item = lseek_it->item.next; free(lseek_it); break; case OP_LLSEEK: llseek_it = (llseek_item_t *) com_it; item = llseek_it->item.next; free(llseek_it); break; case OP_CLONE: clone_it = (clone_item_t *) com_it; item = clone_it->item.next; free(clone_it); break; case OP_MKDIR: mkdir_it = (mkdir_item_t *) com_it; item = mkdir_it->item.next; free(mkdir_it); break; case OP_RMDIR: rmdir_it = (rmdir_item_t *) com_it; item = rmdir_it->item.next; free(rmdir_it); break; case OP_DUP: case OP_DUP2: case OP_DUP3: dup_it = (dup_item_t *) com_it; item = dup_it->item.next; free(dup_it); break; case OP_PIPE: pipe_it = (pipe_item_t *) com_it; item = pipe_it->item.next; free(pipe_it); break; case OP_ACCESS: access_it = (access_item_t *) com_it; item = access_it->item.next; free(access_it); break; case OP_STAT: stat_it = (stat_item_t *) com_it; item = stat_it->item.next; free(stat_it); break; case OP_SOCKET: socket_it = (socket_item_t *) com_it; item = socket_it->item.next; free(socket_it); break; case OP_SENDFILE: sendfile_it = (sendfile_item_t *) com_it; item = sendfile_it->item.next; free(sendfile_it); break; default: ERRORPRINTF("Unknown operation identifier: '%c'\n", com_it->type); return -1; break; } } return 0; } /** Reads integer from string @a str and stores it to the memore referenced by @a num. * This function DOES error checking. And it also skips the last possible space char. * * @arg str buffer from which to read - must be open. * @arg num pointer to the memory where to store number retrieved from the file * @return 0 on success -1 otherwise */ int read_integer(char * str, long long * num) { *num = 0; char digit; int char_read = 0; char c = *str; char_read++; while ( c != 0 && c != ' ' && c != '\n') { *num *= 10; digit= c - '0'; if (digit >=0 && digit <=9) { *num += digit; } else { DEBUGPRINTF("Non digit character while parsing number%s", "\n"); return -1; } str++; c = *str; char_read++; } if (char_read == 1) { return 0; //error } else { return char_read; //including any space character } } int read_open_flag(char * s) { int flag = 0; if (!strcmp(s, "O_APPEND")) { flag |= O_APPEND; } else if ( !strcmp(s, "O_RDONLY")) { flag |= O_RDONLY; } else if ( !strcmp(s, "O_WRONLY")) { flag |= O_WRONLY; } else if ( !strcmp(s, "O_RDWR")) { flag |= O_RDWR; } else if ( !strcmp(s, "O_ASYNC")) { flag |= O_ASYNC; } else if ( !strcmp(s, "O_CLOEXEC")) { //this is only defined on kernels 2.6.23+, which we are not yet using.. DEBUGPRINTF("Unsuported flag: %s\n", "O_CLOEXEC"); } else if ( !strcmp(s, "O_CREAT")) { flag |= O_CREAT; #ifdef _GNU_SOURCE } else if ( !strcmp(s, "O_DIRECT")) { flag |= O_DIRECT; } else if ( !strcmp(s, "O_DIRECTORY")) { flag |= O_DIRECTORY; } else if ( !strcmp(s, "O_LARGEFILE")) { flag |= O_LARGEFILE; } else if ( !strcmp(s, "O_NOATIME")) { flag |= O_NOATIME; } else if ( !strcmp(s, "O_NOFOLLOW")) { flag |= O_NOFOLLOW; #endif } else if ( !strcmp(s, "O_NOCTTY")) { flag |= O_NOCTTY; } else if ( !strcmp(s, "O_EXCL")) { flag |= O_EXCL; } else if ( !strcmp(s, "O_NONBLOCK")) { flag |= O_NONBLOCK; } else if ( !strcmp(s, "O_NDELAY")) { flag |= O_NDELAY; } else if ( !strcmp(s, "O_SYNC")) { flag |= O_SYNC; } else if ( !strcmp(s, "O_TRUNC")) { flag |= O_TRUNC; } return flag; } int read_open_flags(char * str) { int flags = 0; char * s = NULL; s = strtok(str, "|"); while ( s ) { flags |= read_open_flag(s); s = strtok(NULL, "|"); } return flags; } /** Counts number of occurences of char c in string str. * @arg str string in which to search * @arg c char to look for * @return number of occurences of @a c in @a str, or -1 if str is NULL */ int strccount(char * str, char c) { char * s = str - 1; int rv = 0; while(s) { s = strchr(s + 1, c); rv++; } return rv -1; } /** Reads some flag that can be in clone (2) sys call. Only important ones are parsed. */ int read_clone_flag(char * s) { int flag = 0; if (!strcmp(s, "CLONE_FILES")) { flag |= CLONE_FILES; } return flag; } int read_clone_flags(char * str) { int flags = 0; char * s = NULL; s = strtok(str, "|"); while ( s ) { flags |= read_clone_flag(s); s = strtok(NULL, "|"); } return flags; } int read_seek_flag(char * s) { int flag = 0; if (!strcmp(s, "SEEK_SET")) { flag |= SEEK_SET; } else if ( !strcmp(s, "SEEK_CUR")) { flag |= SEEK_CUR; } else if ( !strcmp(s, "SEEK_END")) { flag |= SEEK_END; } return flag; } int read_access_flag(char * s) { int flag = 0; if (!strcmp(s, "F_OK")) { flag |= F_OK; } else if ( !strcmp(s, "R_OK")) { flag |= R_OK; } else if ( !strcmp(s, "W_OK")) { flag |= W_OK; } else if ( !strcmp(s, "X_OK")) { flag |= X_OK; } return flag; } int read_access_flags(char * str) { int flags = 0; char * s = NULL; s = strtok(str, "|"); while ( s ) { flags |= read_access_flag(s); s = strtok(NULL, "|"); } return flags; } int read_dup3_flags(char * str) { #ifdef O_CLOEXEC //2.6.23+ kernels if (!strcmp(str, "O_CLOEXEC")) { return O_CLOEXEC; } else { return 0; } #else return 0; #endif } struct int32timeval read_time(char * timestr) { struct int32timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; char * s = NULL; s = strtok(timestr, "."); if (s) { tv.tv_sec = atoi(s); } else { ERRORPRINTF("Error parsing time, unexpected end of string%s", "\n"); return tv; } s = strtok(NULL, "."); if (s) { tv.tv_usec = atoi(s); } else { ERRORPRINTF("Error parsing time, unexpected end of string%s", "\n"); return tv; } s = strtok(NULL, "."); if (s != NULL) { fprintf(stderr, "Error parsing time, end of string expected!\n"); } return tv; } int32_t read_duration(char * timestr) { int32_t usec = 0; char * s = NULL; s = strtok(timestr, "."); if (s) { usec = atoi(s); } else { ERRORPRINTF("Error parsing time, unexpected end of string: %s\n", timestr); return usec; } usec *= 1000000; s = strtok(NULL, "."); if (s) { usec += atoi(s); } else { ERRORPRINTF("Error parsing time, unexpected end of string:%s\n", timestr); return usec; } s = strtok(NULL, "."); if (s != NULL) { fprintf(stderr, "Error parsing time, end of string expected!\n"); } return usec; } ioapps-1.4-r2/README0000644000175000017500000001436711636305441013456 0ustar keruomkeruomIOapps, an IO profiler and IO traces replayer written by Jiri Horky , 2010, 2011 http://code.google.com/p/ioapps/ Quick description ----------------- IOapps consists of two applications: ioreplay and ioprofiler. Both of them share common code base, the input parsing and handling. ioreplay is mainly intended for replaying of recorded traces, whereas ioprofiler is a GUI application for plotting application's IO access pattern. Both application can currently read strace output produced by: strace -q -a1 -s0 -f -tttT -oOUT_FILE -e trace=file,desc,process,socket APPLICATION ARGUMENTS Both applications can also read binary format of the above data, which is smaller, faster to process and architecture independent. Building: ----------------- To build both application, run: 'make' and to install it: 'make install' If you only want to build or install one of them, run: 'make replay' or 'make profiler' and then: 'make install_replay' or 'make install_profiler'. Features: -------- - multiple strace source possible by design - "raw" strace.file produced by strace -q -a1 -s0 -f -tttT -ostrace.file APPLICATION - binary format. This is recommended, as it is stored in natural - other formats possible by design. - intelligent file descriptor --> file name mapping tracking - process/threads support (copying, sharing file descriptor tables) - duplication of fds, pipe fd... - pipe, socket file descriptor recognition, corresponding reads and writes are not done at all - multiple options for timing of replaying ( ------------------------ Supported syscalls: ------------------------- open close read write lseek _llseek create mkdir access unlink rmdir pipe - this doesn't really call pipe(2) syscall, just keeping track of newly created fds. We don't support IO operations to pipe. socket - similary to pipe, we just keep track of fd mappings dup,dup2,dup3 - this is little bit tricky, as we don't call dup(2) at all. We just keep track of what happened in original process. This is due to our data structures and because it is not necessary to actually call dup, as newly created fd is pointing to the same context. clone - we don't create a new process but just clone/copy it's FD table. Not supported, but should be: ---------------------------- pipe2 pread ! pwrite ! tee splice vmsplice stat fstat ----- Known Limitations: ----- Single threaded as we were trying to keep the thing simple. So it can't really replicate IOs from multiple threads, that are asynchronous and reentrant. It is a big flaw in two views, see them below. But in the practice, one can live with that, if the program he uses is mostly single threaded. 1. This can cause noticeable difference in IO performance. 2. Unfinished/resumed calls handling: Imagine this strace: 18599 1271322908.363138 read(3, 18600 1271322908.363234 write(1, ""..., 4096) = 4096 <0.000008> 18599 1271322908.363264 <... read resumed> ""..., 8192) = 4096 <0.000118> If a processing of interrupted syscalls is switched off, this read will not be caught. If it is enabled (default), there are two approaches for this: First (the bad) one resulting in: 18599 1271322908.363138 read(3, ""..., 8192) = 4096 <0.000118> 18600 1271322908.363234 write(1, ""..., 4096) = 4096 <0.000008> Notice that the first read in process 18599 ended later than the write in process 18600 started: Well, we could loose the exact order of the syscalls, but as we are still single-threaded (and even if we were multi-threaded, it still would be impossible to do it EXACTLY in the right order), it doesn't matter. It would only matters, if those syscall were dependent. This could cause deadlocks in our app. For example: The original read in one process blocks as it waits for data. If everything so far goes as we expected, we would block too. But as we are just single threaded, we will never replicate another write process that has written the data after we are done with processing of the first one. This example is especially true for pipes, but IMHO can be spotted in other situations too. So, we do it other way around: 18600 1271322908.363234 write(1, ""..., 4096) = 4096 <0.000008> 18599 1271322908.363138 read(3, ""..., 8192) = 4096 <0.000118> Please notice, that the start time of the read is not touched, so although it is listed below the write, it has the true start time. When replicating, it should not cause any problems. The only problematic scenario I can think of is following: Process one tries to read from file descriptor, this read is interrupted. Then another process, which shares the descriptor, seeks it somewhere. Then, our read is resumed. We will then misinterpret the read, as the seek will now be before the read. It's even possible that kernel do not allow such behaviour. But it so unlikely, that I am too lazy to write a test program for it. The real solution would be to really be multithreaded, but then more complexity arise... 2. Does not support asynchronous IOs. At all. Yet. 3. Does not support memory mapped files. At all. And it is IMHO not possible without kernel hacking. 4. We do not care about O_CLOEXEC when replicating ... should we? Notes: ------------- Close - behaviour on duplicated fds - one would expect that the close() call will close every copy of the file - which is apparently not true. One needs to call close() for every instance of the previously duplicated fd. So we do it :-) NOTES: -------- When replicating, it may happen that not all actions will be replicated as they should be. For example: imagine some file, created by the program, then closed, and then tried to read again. One would except that open will success as the file should exist. However, some 3rd party application could have deleted the file in the meanwhile. You can not see it from the strace output. Let's say that the original program tried to open the file, it failed and then it created it again. The problem is, that our open call will not fail and thus the other creat (or open with O_CREAT flag) will inherently cause error to our execution. I have decided not to solve it, as it would probably need more time than would be the benefits. Moreover, one can imagine more special cases like that, e.g. with mkdirs or unlinks. This application is not that perfect. ioapps-1.4-r2/rependian.h0000644000175000017500000000337612133726077014717 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _ENDIAN_H_ #define _ENDIAN_H_ #include #ifndef htobe16 #ifdef __USE_BSD /* Conversion interfaces. */ # include # if __BYTE_ORDER == __LITTLE_ENDIAN # define htobe16(x) __bswap_16 (x) # define htole16(x) (x) # define be16toh(x) __bswap_16 (x) # define le16toh(x) (x) # define htobe32(x) __bswap_32 (x) # define htole32(x) (x) # define be32toh(x) __bswap_32 (x) # define le32toh(x) (x) # define htobe64(x) __bswap_64 (x) # define htole64(x) (x) # define be64toh(x) __bswap_64 (x) # define le64toh(x) (x) # else # define htobe16(x) (x) # define htole16(x) __bswap_16 (x) # define be16toh(x) (x) # define le16toh(x) __bswap_16 (x) # define htobe32(x) (x) # define htole32(x) __bswap_32 (x) # define be32toh(x) (x) # define le32toh(x) __bswap_32 (x) # define htobe64(x) (x) # define htole64(x) __bswap_64 (x) # define be64toh(x) (x) # define le64toh(x) __bswap_64 (x) # endif #endif #endif #endif ioapps-1.4-r2/Makefile0000644000175000017500000000214712131056042014216 0ustar keruomkeruom.PHONY : all clean DISTFILES=ioreplay DOCDIR=man MANDIR=/usr/share/man/man1/ IOPROFILER=ioprofiler INSTALL=install TARGET_PATH=/usr/bin SOURCES=ioreplay.c print.c in_common.c in_strace.c in_binary.c replicate.c simulate.c stats.c fdmap.c namemap.c simfs.c adt/list.c adt/hash_table.c adt/fs_trie.c CFLAGS=-c -g -Wall -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -I. -O3 OBJFILES=$(subst .c,.o,$(SOURCES)) DEPFILES=$(subst .c,.d,$(SOURCES)) export MANDIR export INSTALL all: replay profiler profiler: $(MAKE) -C $(IOPROFILER) replay: $(DISTFILES) $(MAKE) -C $(DOCDIR) clean: $(MAKE) -C $(DOCDIR) clean $(MAKE) -C $(IOPROFILER) clean rm -f $(DISTFILES) $(OBJFILES) $(DEPFILES) install: install_replay install_profiler install_replay: replay $(INSTALL) $(DISTFILES) $(TARGET_PATH) $(MAKE) -C $(DOCDIR) install install_profiler: profiler $(INSTALL) ioprofiler-trace $(TARGET_PATH) $(MAKE) -C $(IOPROFILER) install install_ioproftrace: $(DISTFILES): $(subst .c,.o,$(SOURCES)) $(CC) $^ -o $@ %.o: %.c $(CC) $(CFLAGS) $< -o $@ include $(subst .c,.d,$(SOURCES)) %.d: %.c $(CC) -MM -MT "$*.o" $(CPPFLAGS) $< > $*.d ioapps-1.4-r2/man/0000755000175000017500000000000012134167012013330 5ustar keruomkeruomioapps-1.4-r2/man/Makefile0000644000175000017500000000042012131056042014761 0ustar keruomkeruomMANPAGE=ioreplay.1 MANDIR?=/usr/share/man/man1/ INSTALL=install all: $(MANPAGE) $(MANPAGE): help2man --name "IO traces replayer" -s1 -m"User Commands" --output=$(MANPAGE) ../ioreplay -N -i static.txt clean: rm -f $(MANPAGE) install: $(INSTALL) $(MANPAGE) $(MANDIR) ioapps-1.4-r2/man/static.txt0000644000175000017500000000003511463337051015364 0ustar keruomkeruom[SEE ALSO] .BR ioprofiler(1) ioapps-1.4-r2/count.c0000644000175000017500000001515312133726077014071 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "common.h" #include "adt/list.h" #include "in_common.h" #include "fdmap.h" #include "adt/hash_table.h" inline int strace_process_line(FILE * outfile, char * line, hash_table_t * ht); int get_items_strace(char * filename, char * filenameout) ; typedef struct isyscall { item_t item; int32_t pid; char line[MAX_STRING]; } isyscall_t; static int ht_compare_isyscall(key_t *key, item_t *item) { isyscall_t * isyscall; isyscall = hash_table_entry(item, isyscall_t, item); return isyscall->pid == *key; } static inline void ht_remove_callback_isyscall(item_t * item) { isyscall_t * isyscall = hash_table_entry(item, isyscall_t, item); free(isyscall); return; } /** hash table operations. */ static hash_table_operations_t ht_ops_isyscall = { .hash = ht_hash_int, .compare = ht_compare_isyscall, .remove_callback = ht_remove_callback_isyscall /* = NULL if not used */ }; static int ht_compare_str(key_t *key, item_t *item) { str_item_t * str_item; str_item = hash_table_entry(item, str_item_t, item); return ! strncmp(str_item->name, (char *) key, MAX_STRING); } static inline void ht_remove_callback_str(item_t * item) { str_item_t * str_item = hash_table_entry(item, str_item_t, item); //@todo we should free the list before!!! free(str_item); return; } /** hash table operations. */ static hash_table_operations_t ht_ops_str = { .hash = ht_hash_str, .compare = ht_compare_str, .remove_callback = ht_remove_callback_str /* = NULL if not used */ }; char get_operation_code(char * line) { char operation[MAX_STRING]; char * c = line; int i; // skip first part containg PID and time of the syscall while (*c && (isspace(*c) || isdigit(*c) || *c == '.' || *c == '<' )) { c++; } i = 0; while ( *c && (*c) != '(' ) { operation[i] = *c; c++; i++; } operation[i] = 0; } void read_unfinished_strace(char * line, hash_table_t * ht) { isyscall_t * isyscall; item_t * item; int pid; sscanf(line, "%d", &pid); if ( (item = hash_table_find(ht, &pid)) == NULL) { isyscall = malloc(sizeof(isyscall_t)); item_init(&isyscall->item); strncpy(isyscall->line, line, MAX_STRING); isyscall->pid = pid; hash_table_insert(ht, &pid, &isyscall->item); //DEBUGPRINTF("Unfinished call for %d inserted:%s", pid, line); } else { isyscall = hash_table_entry(item, isyscall_t, item); ERRORPRINTF("Already have unfinished syscall for pid: %d. %s", pid, isyscall->line); } } void read_resumed_strace(FILE * f, char * line, hash_table_t * ht) { isyscall_t * isyscall; item_t * item; char buff[MAX_STRING *2]; char *s; int pid; sscanf(line, "%d", &pid); if ( (item = hash_table_find(ht, &pid)) == NULL) { ERRORPRINTF("No syscall to resume for pid: %d. %s", pid, line); } else { isyscall = hash_table_entry(item, isyscall_t, item); s = strstr(isyscall->line, " "); if (s == NULL) { ERRORPRINTF("Previously recorded syscall was wrong: %d. %s", pid, isyscall->line); return; } *s = 0; strncpy(buff, isyscall->line, 2* MAX_STRING); s = strstr(line, "resumed> "); if (s == NULL) { ERRORPRINTF("Resumed syscall incorrectly formated: %d. %s", pid, isyscall->line); return; } s += strlen("resumed> "); strncat(buff, s, 2*MAX_STRING); //DEBUGPRINTF("Resulting line is :%s", buff); hash_table_remove(ht, &pid); strace_process_line(f, buff,ht); } } inline int strace_process_line(FILE * outfile, char * line, hash_table_t * ht) { int retval = 0; char c; char *s; c = get_operation_code(line); if (strstr(line, "unfinished") && c != OP_UNKNOWN) { read_unfinished_strace(line, ht); return 0; } if ((s = strstr(line, "resumed")) != NULL) { if (s !=line) { s--; *s = '('; //lets hack the line, so it recognized if ( get_operation_code(line) != OP_UNKNOWN) { read_resumed_strace(outfile, line, ht); } return 0; } } switch(c) { case OP_WRITE: case OP_READ: case OP_LSEEK: case OP_LLSEEK: case OP_OPEN: case OP_CLOSE: case OP_UNLINK: case OP_CLONE: case OP_DUP3: case OP_DUP2: case OP_DUP: case OP_PIPE: case OP_MKDIR: case OP_ACCESS: case OP_SOCKET: case OP_MMAP: if (strstr(line, "MAP_ANONYMOUS") == NULL) { fprintf(outfile, "%s", line); } break; case OP_UNKNOWN: // just skip unknown operations break; default: return -1; break; } return retval; } /** Reads file syscalls actions from @a filename which is formatted output of strace program. * Detailed format of the input file is described in README under "strace file data structure". * All informations are appended to the @a list. * * @arg filename filename from which to read input * @arg list initialized pointer to the the. * @return 0 on success, error code otherwise */ int get_items_strace(char * filename, char *filenameout) { FILE * f, *fout; char line[MAX_LINE]; hash_table_t ht; int retval; long long linenum = 0; if ( (f = fopen(filename, "r")) == NULL) { DEBUGPRINTF("Error opening file %s: %s\n", filename, strerror(errno)); return errno; } if ( (fout = fopen(filenameout, "w")) == NULL) { DEBUGPRINTF("Error opening file %s: %s\n", filename, strerror(errno)); return errno; } hash_table_init(&ht, HASH_TABLE_SIZE, &ht_ops_isyscall); while(fgets(line, MAX_LINE, f) != NULL) { linenum++; retval = strace_process_line(fout, line, &ht); if (retval != 0) { ERRORPRINTF("Error parsing file %s: on line %lld, position %ld\n", filename, linenum, ftell(f)); } if ( linenum % 100000 == 0) { printf("line num: %lld\n", linenum); } } printf("line num: %lld\n", linenum); return 0; } int main(int argc, char * * argv) { if (argc != 3) { fprintf(stderr, " needed\n"); return EXIT_FAILURE; } else { get_items_strace(argv[1], argv[2]); } return 0; } ioapps-1.4-r2/COPYING0000644000175000017500000004046012133726077013627 0ustar keruomkeruomThis is the copyright file for IOApps, which must be retained as is. Contact the author for multilicencing options. Copyright (c) 2010 Jiri Horky GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. ioapps-1.4-r2/simfs.h0000644000175000017500000000563512133726077014073 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _SIM_FS_ #define _SIM_FS_ /** @file simfs.h * * The SimFS module is inteded for simulating FS system calls such as access/mkdir/unlink/creat and maybe others in future. * The purpose of it is tracking down inconsistencies with current file system and file system used by straced process - it * should warn you about files/directories that are not present and are needed by straced process (e.g. it tries to create * file in directory that doesn't exist). It also warns about files/directories that exist but should not (i.e. system call * in previous process failed, but wouldn't fail now). * * It works by dynamic scanning of actual file system when a call to access it is needed. That said, it didn't create whole * file-system structure, but only parts that are actualy needed. * * If a system call fails, (e.g. mkdir in directory that doesn't exist yet), the SimFS returns error and creates appropriate * records as it would exist. This way, one error doesn't cause chain of other related problems. * */ #include "adt/fs_trie.h" #include "common.h" #define SIMFS_OK 0 #define SIMFS_PHYS -1 #define SIMFS_VIRT -2 #define SIMFS_ENOENT 1 #define SIMFS_EENT 2 typedef struct simfs { char type; ///< whether I am directory or file char physical; ///< whether it exists on disk char created; ///< whether this file was created by replicating (ie by creat/mkdir call) uint64_t phys_size; //physical size on the disk (+ something, in case of writes), or -1 if virtual only uint64_t virt_size; trie_node_t node; ///< I am part of the node } simfs_t; void simfs_init(); void simfs_finish(); void simfs_dump(); int simfs_stat(stat_op_t * stat_op); int simfs_access(access_op_t * access_op); int simfs_mkdir(mkdir_op_t * mkdir_op); int simfs_rmdir(rmdir_op_t * rmdir_op); int simfs_unlink(unlink_op_t * unlink_op); int simfs_creat(open_op_t * open_op); int simfs_has_file(const char * name); simfs_t * simfs_find(const char * name); void simfs_apply(void (* function)(simfs_t * item)); void simfs_apply_full_name(void (* function)(simfs_t * item, char * full_name)); int simfs_is_file(simfs_t * simfs); #endif ioapps-1.4-r2/namemap.h0000644000175000017500000000257712133726077014372 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _NAMEMAP_H_ #define _NAMEMAP_H_ /** @file namemap.h * * Takes care of mapping file names to null string (when given file shoud be ignores) or to another filename. * * It uses hash_table, where the key is an original filename and data new filenames. */ #include #include "adt/hash_table.h" #include "adt/list.h" typedef struct namemap_item { item_t item; char old_name[MAX_STRING]; char new_name[MAX_STRING]; } namemap_item_t; int namemap_init(char *ifilename, char *mfilename); char * namemap_get_name(char * name); void namemap_finish(); #endif ioapps-1.4-r2/simfs.c0000644000175000017500000005614712133726077014072 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "simfs.h" #include "in_common.h" #include "ioreplay.h" trie_t * fs; int simfs_mask; void (* simfs_apply_function)(simfs_t * simfs); void (* simfs_apply_function_full)(simfs_t * simfs, char * full_name); trie_node_t * simfs_new_trie_node() { simfs_t * simfs = malloc(sizeof(simfs_t)); simfs->physical = 0; simfs->phys_size = -1; simfs->virt_size = 0; simfs->created = 0; return & simfs->node; } trie_node_t * simfs_new_trie_node_phys() { simfs_t * simfs = malloc(sizeof(simfs_t)); simfs->physical = 1; simfs->phys_size = 0; simfs->virt_size = 0; simfs->created = 0; return & simfs->node; } void simfs_delete_trie_node(trie_node_t * node) { simfs_t * simfs = trie_get_instance(node, simfs_t, node); free(node->key); free(simfs); } /** Polish path name, ie. it destroys all '..' and '.' in path and also make sure the path is absolute, * ie. current directory is prepended to the path. * @arg name path name to be polished * @return absolute path name of the name without any ".." in it. */ void simfs_absolute_name(const char * name, char * buff, int size) { char * s; strcpy(buff,name); if (name[0] != '/') {//not absolute path if ( getcwd(buff, size) == NULL) { ERRORPRINTF("Current path dir exceeds compiled maximum of %d bytes. Recompile with bigger limit.\n", size); exit(0); } if (strlen(buff) + strlen(name) + 1 + 1 > size) { ERRORPRINTF("Current path name+ access path name exceeds compiled maximum of %d bytes. Recompile with bigger limit.\n", size); exit(0); } strcat(buff, "/"); strcat(buff, name); } else { strcpy(buff, name); } char * ss; int off; while ((s = strstr(buff, "/..")) != NULL ) { //directory name can't have "/.." in its name off = (int)(s - buff); if (off == 0) { //beggining of the string s = buff + 3; int i = 0; while (*s) { buff[i] = *s; i++; s++; } buff[i] = 0; } else { // get rid of it ss = s+3; //one char after "/.." *s = 0; s = rindex(buff, '/'); assert(s); //there must be one... while (*ss) { *s = *ss; s++; ss++; } *s = 0; } } while ( (s = strstr(buff, "/.")) != NULL ) { //directory name can't have "/." in its name if (s == buff) { //beggining of the string if ( strlen(buff) == 2 ) { //only this strcpy(buff, "/"); } else { s = buff + 2; int i = 0; while (*s) { buff[i] = *s; i++; s++; } buff[i] = 0; } } else { // get rid of it ss = s+2; // end od/beggining of '/.' while(*ss) { *s = *ss; s++; ss++; } *s = 0; } } } /** Checks whether all @a missing directories/files exists on disks starting from path @a existing. * @arg existing where to start checking * @arg missing missing part to check * @return 1 if all exists, 0 if not. */ int simfs_populate(char * existing, char * missing) { int rv; char * strtmp; char * saveptr; trie_node_t * n; struct stat st; char * s; char * c; int i; short slash_added = 0; //char name[MAX_LINE]; strtmp = strdup(missing); s = strtok_r(strtmp, "/", &saveptr); int mis_off = 0; rv = 1; while(s) { int l = strlen(existing); c = existing + l; //end of buff string slash_added = 0; if (! *existing || existing[l-1] != '/') { //if it is empty string or there isn't ending slash character strcat(existing, "/"); slash_added = 1; } strcat(existing, s); if ( access(existing, F_OK) == 0) { //it exists on the disk n = trie_insert2(fs, existing, simfs_new_trie_node_phys); simfs_t * simfs = trie_get_instance(n, simfs_t, node); stat(existing, &st); simfs->phys_size = st.st_size; } else { *c = 0; //cut off last non-existing part rv = 0; break; } mis_off += slash_added + strlen(s); s = strtok_r(NULL, "/", &saveptr); } //polish also the missing string i = 0; if (mis_off != 0) { while(missing[mis_off + i]) { missing[i] = missing[mis_off+i]; i++; } missing[i] = 0; } free(strtmp); return rv; } /** Inits all structures needed by simfs. You have to call this function before using other simfs_* functions. */ void simfs_init(int mask) { simfs_t * simfs; fs = malloc(sizeof(trie_t)); trie_init(fs, '/', simfs_new_trie_node, simfs_delete_trie_node); simfs = trie_get_instance(fs->root, simfs_t, node); simfs->physical = 1; simfs_mask = mask; } /** Frees all structure used by SimFs. Call this function when you will not access SimFS any longer. */ void simfs_finish() { ///< @todo destroy trie trie_destroy(fs); simfs_mask = 0; free(fs); } /** Simulates access(2) system call on the SimFS. It takes appropriate actions in to fix fs, if needed. * * @arg access_op structure with all information about the call. It contains also return code that is expected. If * zero, access should succeed, if non-zero, the file should not exists. In fact, there many other reasons * for non-zero return value, but we are just not that smart (yet?). * @return zero, if everything went ok. SIMFS_ENOENT if the @a name doesn't exists, SIMFS_EENT if the @a name exists but it should not. */ int simfs_access(access_op_t * access_op) { trie_node_t * node; simfs_t * simfs; int rv; int i = 0; char * missing; char name_buff[MAX_LINE]; char * name; simfs_absolute_name(access_op->name, name_buff, MAX_LINE); name = name_buff; char * buff = strdup(name); missing = strdup(name); node = trie_longest_prefix(fs, name, buff); simfs = trie_get_instance(node, simfs_t, node); if (strcmp(name, buff) == 0) { //the file/directory is there if (access_op->retval == 0) { //and it was expected rv = 0; } else { if (simfs->physical) { ERRORPRINTF("Previous access call to %s failed, but we would succeed. Delete the file %s.\n", name, name); trie_delete(fs, name); if (simfs_mask & ACT_PREPARE) { ///< @todo really delete it } rv = SIMFS_EENT; } else { // this is really strange, it only exists in virtual space, i.e. we created the file and now it should // exist there...source strace file corrupted? ERRORPRINTF("Previous access call to %s failed but the file was created by replicating. Corrupted source .strace file?\n", name); rv = SIMFS_EENT; } } } else { //the file is not there while (buff[i] && buff[i] == name[i]) //skip common part i++; strcpy(missing, name + i); int exists = simfs_populate(buff, missing); if (access_op->retval != 0 ) {//previous access call failed if ( exists ) { //would we fail? ERRORPRINTF("Previous access call to %s failed but we would succeed. Delete the file %s.\n", name, name); trie_delete(fs,name); // delete last node rv = SIMFS_EENT; } else { rv = 0; } } else { if ( ! exists) { ERRORPRINTF("2File %s doesn't exist, only '%s' exists, create missing entries (%s): %s\n", name, buff, missing, strerror(errno)); trie_insert(fs, name); if (simfs_mask & ACT_PREPARE) { ///< @todo really create it } } else { rv = 0; } } } free(buff); return rv; } /** Simulates stat(2) system call on the SimFS. It takes appropriate actions in to fix fs, if needed. * * @arg stat_op structure with all information about the call. It contains also return code that is expected. If * zero, stat should succeed, if non-zero, the file should not exists. In fact, there many other reasons * for non-zero return value, but we are just not that smart (yet?). * @return zero, if everything went ok. SIMFS_ENOENT if the @a name doesn't exists, SIMFS_EENT if the @a name exists but it should not. */ int simfs_stat(stat_op_t * stat_op) { trie_node_t * node; simfs_t * simfs; int rv; int i = 0; char * missing; char name_buff[MAX_LINE]; char * name; simfs_absolute_name(stat_op->name, name_buff, MAX_LINE); name = name_buff; char * buff = strdup(name); missing = strdup(name); node = trie_longest_prefix(fs, name, buff); simfs = trie_get_instance(node, simfs_t, node); if (strcmp(name, buff) == 0) { //the file/directory is there if (stat_op->retval == 0) { //and it was expected rv = 0; } else { if (simfs->physical) { ERRORPRINTF("Previous stat call to %s failed, but we would succeed. Delete the file %s.\n", name, name); trie_delete(fs, name); if (simfs_mask & ACT_PREPARE) { ///< @todo really delete it } rv = SIMFS_EENT; } else { // this is really strange, it only exists in virtual space, i.e. we created the file and now it should // exist there...source strace file corrupted? ERRORPRINTF("Previous stat call to %s failed but the file was created by replicating. Corrupted source .strace file?\n", name); rv = SIMFS_EENT; } } } else { //the file is not there while (buff[i] && buff[i] == name[i]) //skip common part i++; strcpy(missing, name + i); int exists = simfs_populate(buff, missing); if (stat_op->retval != 0 ) {//previous stat call failed if ( exists ) { //would we fail? ERRORPRINTF("Previous stat call to %s failed but we would succeed. Delete the file %s.\n", name, name); trie_delete(fs,name); // delete last node rv = SIMFS_EENT; } else { rv = 0; } } else { if ( ! exists) { ERRORPRINTF("2File %s doesn't exist, only '%s' exists, create missing entries (%s): %s\n", name, buff, missing, strerror(errno)); trie_insert(fs, name); if (simfs_mask & ACT_PREPARE) { ///< @todo really create it } rv = SIMFS_ENOENT; } else { rv = 0; } } } free(buff); return rv; } /** Simulates mkdir(2) system call on the SimFS. It takes appropriate actions to fix the fs, if needed. * @arg mkdir_op structure with all information about the call. * * @return zero, if successful, SIMFS_ENOENT if component in path doesn't exist or SIMFS_EENT if the dir itself exists. */ int simfs_mkdir(mkdir_op_t * mkdir_op) { trie_node_t * node; simfs_t * simfs; int rv; int i = 0; char * missing; char name_buff[MAX_LINE]; char * name; simfs_absolute_name(mkdir_op->name, name_buff, MAX_LINE); name = name_buff; char * buff = strdup(name); missing = strdup(name); node = trie_longest_prefix(fs, name, buff); simfs = trie_get_instance(node, simfs_t, node); while (buff[i] && buff[i] == name[i]) //skip common part i++; strcpy(missing, name + i); simfs_populate(buff, missing); //make sure that buff and missing are reflecting current situation if (strcmp(name, buff) == 0) { //the directory is already there if (mkdir_op->retval != 0) { //and previous mkdir failed (maybe because it was there already) rv = 0; } else { //but previous mkdir succeeded if (simfs->physical) { ERRORPRINTF("Previous mkdir call of %s succeeded. But the dir already exists. Delete it.\n", name); if (simfs_mask & ACT_PREPARE) { ///< @todo really delete it } rv = SIMFS_EENT; } else { // this is really strange, it only exists in virtual space, i.e. we created the directory but it // shouldn't... ERRORPRINTF("Previous mkdir call of %s succeeded but the dir already exists and was created by us. Corrupted source .strace file?\n", name); rv = SIMFS_EENT; } } } else { //dir is not there char would_succ; ///< whether the missing part is only new directory itself int len = strlen(missing); if ( missing[len-1] == '/' ) { missing[len-1] = 0; //delete last '/' char } if ( missing[0] == '/' ) { missing += 1; } int count = strccount(missing, '/'); //this is really simplistic, not taking permissions into account... if (count == 0 ) { would_succ = 1; } else { would_succ = 0; } if (mkdir_op->retval != 0 ) {//previous mkdir call failed if ( would_succ == 0) { // and so we would... rv = 0; } else { if (simfs->physical) { ERRORPRINTF("Previous mkdir call to %s failed but we would succeed.\n", name); } else { ERRORPRINTF("Previous mkdir call to %s failed but we would succeed and it was me who created the path. Corrupted source .strace file?\n", name); } trie_delete(fs, name); rv = SIMFS_EENT; } } else { //previous mkdir call succeeded if ( would_succ) { rv = 0; } else { //would_succ == 0 ERRORPRINTF("Mkdir can't succeed as the path is not ready. Only '%s' exists, create missing entry for mkdir of (%s)\n", buff, name); if (simfs_mask & ACT_PREPARE) { ///< @todo really create it } rv = SIMFS_ENOENT; } trie_node_t * n = trie_insert(fs, name); simfs_t * simfs = trie_get_instance(n, simfs_t, node); simfs->created = 1; } } free(buff); return rv; } /** Simulates unlink(2) system call on the SimFS. It takes appropriate actions to fix the fs, if needed. * @arg unlink_op structure with all information about the call. * @return zero if successful, SIMFS_ENOENT in case of missing file/dir and SIMFS_EENT in case of previously failed * unlink operation that would now succeed. */ int simfs_unlink(unlink_op_t * unlink_op) { trie_node_t * node; simfs_t * simfs; int rv; int i = 0; char * missing; char name_buff[MAX_LINE]; char * name; simfs_absolute_name(unlink_op->name, name_buff, MAX_LINE); name = name_buff; char * buff = strdup(name); missing = strdup(name); node = trie_longest_prefix(fs, name, buff); simfs = trie_get_instance(node, simfs_t, node); if (strcmp(name, buff) == 0) { //the file/directory is there if (unlink_op->retval == 0) { //and it was expected trie_delete(fs, name); rv = 0; } else { if (simfs->physical) { ERRORPRINTF("Previous unlink call to %s failed but we would (probably) succeed. Delete the file.\n", name); if (simfs_mask & ACT_PREPARE) { ///< @todo really delete it } rv = SIMFS_EENT; } else { // this is really strange, it only exists in virtual space, i.e. we created the file and now it should // exist there...source strace file corrupted? ERRORPRINTF("Previous unlink call to %s failed but the file was created by replicating. Corrupted source .strace file?\n", name); rv = SIMFS_EENT; } trie_delete(fs, name); } } else { //the file is not there while (buff[i] && buff[i] == name[i]) //skip common part i++; strcpy(missing, name + i); int exists = simfs_populate(buff, missing); if (unlink_op->retval != 0 ) {//previous unlink call failed, and probably we would fail too... if ( exists ) { //would we really fail? ERRORPRINTF("Previous unlink call to %s failed, but we would succeed. Delete the file %s.\n", name, name); trie_delete(fs, name); rv = SIMFS_EENT; } else { rv = 0; } } else { //unlink succeeded if ( ! exists ) { // we created the longest prefix --> longer path cannot exist ERRORPRINTF("1File %s doesn't exist, only '%s' exists, create missing entries (%s)\n", name, buff, missing); trie_insert(fs, name); //create all missing entries trie_delete(fs, name); if (simfs_mask & ACT_PREPARE) { ///< @todo really create it } rv = SIMFS_ENOENT; } else { //shorter path existed, maybe we have it on disk? ERRORPRINTF("2File %s doesn't exist, only '%s' exists, create missing entries (%s): %s\n", name, buff, missing, strerror(errno)); trie_insert(fs, name); trie_delete(fs, name); //it was created in simfs so delete it if (simfs_mask & ACT_PREPARE) { ///< @todo really create it } rv = SIMFS_ENOENT; } } } free(buff); return rv; } /** Simulates rmdir(2) system call on the SimFS. It takes appropriate actions to fix the fs, if needed. * @arg rmdir_op structure with all information about the call. The implementation is VERY simplistic. * @return zero if successful, SIMFS_ENOENT in case of missing file/dir and SIMFS_EENT in case of previously failed * rmdir operation that would now succeed. */ int simfs_rmdir(rmdir_op_t * rmdir_op) { ///< @todo rewrite. For now it is sufficient as we don't have information whether given node is a directory or a file, //but this is really nasty. int rv; unlink_op_t * unlink_op = malloc(sizeof(unlink_op_t)); unlink_op->retval = rmdir_op->retval; strcpy(unlink_op->name, rmdir_op->name); unlink_op->info = rmdir_op->info; rv = simfs_unlink(unlink_op); free(unlink_op); return rv; } /** Simulates open(2)/creat(2) system call on the SimFS. It takes appropriate actions to fix the fs, if needed. * @arg open_op structure with all information about the call. * @return zero if successful, ENOENT in case of error. */ int simfs_creat(open_op_t * open_op) { trie_node_t * node; simfs_t * simfs; int rv; int i = 0; char * missing; char name_buff[MAX_LINE]; char * name; simfs_absolute_name(open_op->name, name_buff, MAX_LINE); name = name_buff; char * buff = strdup(name); missing = strdup(name); node = trie_longest_prefix(fs, name, buff); simfs = trie_get_instance(node, simfs_t, node); while (buff[i] && buff[i] == name[i]) //skip common part i++; strcpy(missing, name + i); simfs_populate(buff, missing); //make sure buff and missing reflect actual situation on the disk if (strcmp(name, buff) == 0) { //the file is already there if (open_op->flags & O_CREAT) { if ( open_op->flags & O_EXCL) { if (open_op->retval == -1) { //and previous open failed (probably because it was there already) rv = 0; } else { //but previous open succeeded if (simfs->physical) { ERRORPRINTF("Previous open call (with O_EXCL) of %s succeeded. But the file already exists. Delete it.\n", name); if (simfs_mask & ACT_PREPARE) { ///< @todo really delete it } rv = SIMFS_EENT; } else { // this is really strange, it only exists in virtual space, i.e. we created the directory but it // shouldn't... ERRORPRINTF("Previous open call (with O_EXCL) of %s suceeded. But the file was created by replicating. Corrupted source .strace file?\n", name); rv = SIMFS_EENT; } } } else { //Creating new file, without O_EXCL, file is there --> everything ok. rv = 0; } } else { //Not O_CREAT if (open_op->retval == -1) { //and previous open failed if (simfs->physical) { ERRORPRINTF("Previous open call of %s failed. But we would succeed. Delete the file?.\n", name); trie_delete(fs, name); if (simfs_mask & ACT_PREPARE) { ///< @todo really delete it } rv = SIMFS_EENT; } else { // this is really strange, it only exists in virtual space, i.e. we created the directory but it // shouldn't... ERRORPRINTF("Previous open call to %s failed but the file was created by replicating. Corrupted source .strace file?\n", name); trie_delete(fs, name); rv = SIMFS_EENT; } } else { //previous open succeeded rv = 0; } } } else { //file is not yet there char would_succ; ///< whether the missing part is only new file itself if ( missing[0] == '/' ) { missing += 1; // "delete" '/' char } int count = strccount(missing, '/'); //this is really simplistic, not taking permissions into account... if (count == 0) { would_succ = 1; } else { would_succ = 0; } if ( ! (open_op->flags & O_CREAT)) { //not creating new file --> fail if (open_op->retval == -1) {//previous open call failed rv = 0; } else { ERRORPRINTF("Open can't succeed as the file is not there. Only '%s' exists, create missing entry for open of (%s)\n", buff, name); trie_insert(fs, name); rv = SIMFS_ENOENT; } } else { // creating new file if (open_op->retval == -1) {//previous open call failed if ( would_succ == 0) { // and so we would... rv = 0; } else { if (simfs->physical) { ERRORPRINTF("Previous creatcall to %s failed, but we would succeed.\n", name); } else { ERRORPRINTF("Previous creat call to %s failed, but we would succeed and it was me who created the path. Corrupted source .strace file?\n", name); } trie_delete(fs, name); rv = SIMFS_EENT; } } else { //previous open call succeeded if ( would_succ) { rv = 0; } else { //would_succ == 0 ERRORPRINTF("Creat can't succeed as the path is not ready. Only '%s' exists, create missing entry for creat of (%s)\n", buff, name); if (simfs_mask & ACT_PREPARE) { ///< @todo really create it } rv = SIMFS_ENOENT; } trie_node_t * n = trie_insert(fs, name); simfs_t * simfs = trie_get_instance(n, simfs_t, node); simfs->created = 1; } } } free(buff); return rv; } /** Checks whether given file @a name exists in virutal filesystem and if it was only virtually created or it * exists on the disk too. * * @arg name filename to be check * @return SIMFS_PHYS if file physically existed, SIFMS_VIRT if it was created during simulation or SIMFS_ENOENT if * file doesn't exist */ int simfs_has_file(const char * name) { trie_node_t * node; simfs_t * simfs; int rv; char * buff = strdup(name); node = trie_longest_prefix(fs, name, buff); simfs = trie_get_instance(node, simfs_t, node); if (strcmp(name, buff) == 0) { //the file is already there if (simfs->physical) { rv = SIMFS_PHYS; } else { rv = SIMFS_VIRT; } } else { rv = SIMFS_ENOENT; } free(buff); return rv; } /** Returns whether given simfs item @a simfs is file or not. It is quite silly, it only checks if it has * some children or not. @todo make it more clever... * * @arg simfs pointer to item to be checked * @return true if argument is file, false otherwise */ int simfs_is_file(simfs_t * simfs) { return trie_is_leaf(&simfs->node); } simfs_t * simfs_find(const char * name) { char name_buff[MAX_LINE]; simfs_absolute_name(name, name_buff, MAX_LINE); trie_node_t * node = trie_find(fs, name_buff); simfs_t * simfs; if (!node) { return NULL; } else { simfs = trie_get_instance(node, simfs_t, node); return simfs; } } void simfs_apply_wrapper_full(trie_node_t * node, char * full_name) { simfs_t * simfs = trie_get_instance(node, simfs_t, node); simfs_apply_function_full(simfs, full_name); } void simfs_apply_wrapper(trie_node_t * node) { simfs_t * simfs = trie_get_instance(node, simfs_t, node); simfs_apply_function(simfs); } void simfs_apply(void (* function)(simfs_t * item)) { simfs_apply_function = function; trie_apply(fs, simfs_apply_wrapper); } void simfs_apply_full_name(void (* function_full)(simfs_t * item, char * full_name)) { simfs_apply_function_full = function_full; trie_apply_full(fs, simfs_apply_wrapper_full); } void simfs_dump() { trie_dump(fs); } ioapps-1.4-r2/in_binary.h0000644000175000017500000000174212133726077014717 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _REPIO_BINARY_H_ #define _REPIO_BINARY_H_ int bin_save_items(char * filename, list_t * list); int bin_get_items(char * filename, list_t * list); #endif ioapps-1.4-r2/namemap.c0000644000175000017500000001502312133726077014353 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "common.h" #include "namemap.h" hash_table_t ht_map; list_t l_igns; static int ht_compare_namemap(key_t *key, item_t *item) { namemap_item_t * namemap_item; namemap_item = hash_table_entry(item, namemap_item_t, item); return ! strncmp(namemap_item->old_name, (char *) key, MAX_STRING); } static inline void ht_remove_callback_namemap(item_t * item) { namemap_item_t * namemap_item = hash_table_entry(item, namemap_item_t, item); free(namemap_item); return; } /** hash table operations. */ static hash_table_operations_t ht_ops_namemap = { .hash = ht_hash_str, .compare = ht_compare_namemap, .remove_callback = ht_remove_callback_namemap }; char * namemap_load_item(char * line, char * name, ssize_t size) { int i = 0; if ( line[0] == '"' ) { i = 1; while (line[i] && line[i] != '"' && i < size) { name[i-1] = line[i]; i++; } if (! line[i]) { //we reached end of the line ERRORPRINTF("Error loading mapping from file: Missing '\"' character.%s", "\n"); return NULL; } else if ( i >= MAX_STRING ) { ERRORPRINTF("Error loading mapping from file: Missing '\"' character or path name too long %s", "\n"); return NULL; } name[i-1] = 0; return line + (i+1); } else if ( isalnum(line[0]) || line[0] == '/' ) { i = 0; while (line[i] && line[i] != ' ' && i < size) { name[i] = line[i]; i++; } if ( i >= MAX_STRING ) { ERRORPRINTF("Error loading mapping from file: Missing ' ' character or path name too long on line: %s", "\n"); return NULL; } name[i] = 0; return line + i; } else { ERRORPRINTF("Error loading mapping from file: First character on the line unrecognized: '%c'(%d)\n", line[0], line[0]); return NULL; } } int namemap_load_items(char * line, char * old_name, char * new_name, ssize_t size) { char * c; if ( (c = namemap_load_item(line, old_name, size)) == NULL) { return -1; } else { if ( (c = namemap_load_item(c+1, new_name, size)) == NULL) { return -1; } else { if ( *c != 0 ) { ERRORPRINTF("Error loading mapping from file: not whole line was read!%s", "\n"); return -1; } else { return 0; } } } } int namemap_init(char * ifilename, char * mfilename) { FILE * ifile = NULL; FILE * mfile = NULL; char line[2*MAX_STRING + 2]; int linenum = 1; namemap_item_t * nm_item; hash_table_init(&ht_map, HASH_TABLE_SIZE, &ht_ops_namemap ); list_init(&l_igns); if (ifilename) { if ( (ifile = fopen(ifilename, "r")) == NULL) { ERRORPRINTF("Cannot open ignore file %s: %s. Ignoring it.\n", ifilename, strerror(errno)); return -1; } } if (mfilename) { if ( (mfile = fopen(mfilename, "r")) == NULL) { ERRORPRINTF("Cannot open mapping file %s: %s. Ignoring it.\n", mfilename, strerror(errno)); return -1; } } if (ifile) { while(fgets(line, MAX_STRING, ifile) != NULL) { if ( line[0] == '#' ) //comment continue; nm_item = malloc(sizeof(namemap_item_t)); item_init(&nm_item->item); int i = 0; int found_nl = 0; while ( i < MAX_STRING && line[i] != 0) { if (line[i] == '\n') { line[i] = 0; found_nl = 1; break; } i++; } if ( ! found_nl) { ERRORPRINTF("Error loading ignored file names from %s: line %d too long. \n", ifilename, linenum); free(nm_item); return -1; } strncpy(nm_item->old_name, line, MAX_STRING); nm_item->new_name[0] = 0; list_append(&l_igns, &nm_item->item); linenum++; } } linenum = 1; if (mfile) { while(fgets(line, 2*MAX_STRING+2, mfile) != NULL) { if ( line[0] == '#' ) //comment continue; nm_item = malloc(sizeof(namemap_item_t)); item_init(&nm_item->item); int i = 0; int found_nl = 0; while ( i < MAX_STRING && line[i] != 0) { if (line[i] == '\n') { line[i] = 0; found_nl = 1; break; } i++; } if ( ! found_nl) { ERRORPRINTF("Error loading ignored file names from %s: line %d too long. \n", ifilename, linenum); free(nm_item); return -1; } nm_item = malloc(sizeof(namemap_item_t)); item_init(&nm_item->item); if ( namemap_load_items(line, nm_item->old_name, nm_item->new_name, MAX_STRING) != 0) { ERRORPRINTF("Error occurred reading file %s on line %d.\n", mfilename, linenum); free(nm_item); return -1; } hash_table_insert(&ht_map, (key_t *) nm_item->old_name, &nm_item->item); linenum++; } } return 0; } /** Deallocates structures used by this file. * */ void namemap_finish() { hash_table_destroy(&ht_map); namemap_item_t * nm_item; item_t * item = l_igns.head; while (item) { nm_item = list_entry(item, namemap_item_t, item); item = item->next; free(nm_item); } } /** Searches for mapping for given filename. It returns NULL if the file should be ignored, * mapped file name in case mapping was found or simply @a name if no change is necessary. * * @arg name file name for which to find mapping * @return old/new filename or NULL if the file should be ignored */ char * namemap_get_name(char * name) { namemap_item_t * nm_item; item_t * item = l_igns.head; int rv; while (item) { nm_item = list_entry(item, namemap_item_t, item); rv = fnmatch(nm_item->old_name, name, 0); if (rv == 0) { return NULL; } else if ( rv != FNM_NOMATCH ) { ERRORPRINTF("Error occured during matching name %s to string %s.\n", name, nm_item->old_name); return NULL; // it will be best to ignore this file... } item = item->next; } //it was not in ignore items, maybe it is in mapped items item = hash_table_find(&ht_map, (key_t *) name); if (item) { nm_item = list_entry(item, namemap_item_t, item); return nm_item->new_name; } else { //no change necessary: return name; } } ioapps-1.4-r2/replicate.h0000644000175000017500000000242612133726077014715 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _REPLICATE_H_ #define _REPLICATE_H_ #include #include "common.h" #include "in_common.h" //ignore this operation - do not replicate it #define O_IGNORE 020000000000 //31st bit #define S_IFIGNORE ((1 << 30)-1) // first 30 bits are 1 int replicate(list_t * list, int cpu, double scale, int sim_mode, char * ifile, char * mfile); void replicate_clone(clone_item_t * op_it, int op_mask); void replicate_open(open_item_t * op_it, int op_mask); #endif ioapps-1.4-r2/in_strace.h0000644000175000017500000000227212133726077014713 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "in_common.h" #include "adt/list.h" typedef struct isyscall { item_t item; int32_t pid; char line[MAX_STRING]; } isyscall_t; int strace_get_items(char * filename, list_t * list, int stats); inline int strace_process_line(char * line, list_t * list, hash_table_t * ht, int stats); ioapps-1.4-r2/in_binary.c0000644000175000017500000006117312133726077014716 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "rependian.h" #include "common.h" #include "in_common.h" #include "adt/list.h" #include "adt/hash_table.h" #define BIN_READ_ERROR_FREE ERRORPRINTF("Error reading event (%c) structure number: %"PRIi64"\n", c, num); \ free(op_it); \ return -1 #define BIN_READ_ERROR ERRORPRINTF("Error reading event (%c) structure number: %"PRIi64"\n", c, num); \ return -1 #define BIN_WRITE_ERROR ERRORPRINTF("Error writing event. Retval: %d\n", rv); \ return -1 #define read_int32(var) \ if ( (rv = fread(&i32, sizeof(int32_t), 1, f)) != 1 ) { \ BIN_READ_ERROR_FREE; \ } else { \ var = le32toh(i32); \ } #define read_int64(var) \ if ( (rv = fread(&i64, sizeof(int64_t), 1, f)) != 1 ) { \ BIN_READ_ERROR_FREE; \ } else { \ var = le64toh(i64); \ } #define write_int32(var) \ i32 = htole32(var); \ if ( (rv = fwrite(&i32, sizeof(int32_t), 1, f)) != 1 ) { \ BIN_WRITE_ERROR; \ } #define write_int64(var) \ i64 = htole64(var); \ if ( (rv = fwrite(&i64, sizeof(int64_t), 1, f)) != 1 ) { \ BIN_WRITE_ERROR; \ } #define write_char(var) \ if ( (rv = fwrite(&var, sizeof(char), 1, f)) != 1 ) { \ BIN_WRITE_ERROR; \ } #define write_string(str, size) \ if ( (rv = fwrite(str, sizeof(char), size, f)) != size ) { \ BIN_WRITE_ERROR; \ } inline int bin_read_info(FILE *f, op_info_t * info, char c, int64_t num) { int32_t i32; int rv; if ( (rv = fread(&i32, sizeof(int32_t), 1, f)) != 1 ) { BIN_READ_ERROR; } else { info->pid = le32toh(i32); } if ( (rv = fread(&i32, sizeof(int32_t), 1, f)) != 1 ) { BIN_READ_ERROR; } else { info->dur = le32toh(i32); } if ( (rv = fread(&i32, sizeof(int32_t), 1, f)) != 1 ) { BIN_READ_ERROR; } else { info->start.tv_sec = le32toh(i32); } if ( (rv = fread(&i32, sizeof(int32_t), 1, f)) != 1 ) { BIN_READ_ERROR; } else { info->start.tv_usec = le32toh(i32); } return 0; } int bin_read_read(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; int64_t i64; char c = OP_READ; read_item_t * op_it; op_it = new_read_item(); op_it->type = c; read_int32(op_it->o.fd); read_int64(op_it->o.size); read_int64(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_write(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; int64_t i64; char c = OP_WRITE; write_item_t * op_it; op_it = new_write_item(); op_it->type = c; read_int32(op_it->o.fd); read_int64(op_it->o.size); read_int64(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_pread(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; int64_t i64; char c = OP_PREAD; pread_item_t * op_it; op_it = new_pread_item(); op_it->type = c; read_int32(op_it->o.fd); read_int64(op_it->o.size); read_int64(op_it->o.offset); read_int64(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_pwrite(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; int64_t i64; char c = OP_PWRITE; pwrite_item_t * op_it; op_it = new_pwrite_item(); op_it->type = c; read_int32(op_it->o.fd); read_int64(op_it->o.size); read_int64(op_it->o.offset); read_int64(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_open(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; char c = OP_OPEN; char buff[MAX_STRING]; open_item_t * op_it; op_it = new_open_item(); op_it->type = c; int32_t len; read_int32(len); if ( (rv = fread(buff, sizeof(char), len, f)) != len ) { BIN_READ_ERROR_FREE; } else { buff[len] = 0; strncpy(op_it->o.name, buff, len+1); } read_int32(op_it->o.flags); read_int32(op_it->o.mode); read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_close(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; char c = OP_CLOSE; close_item_t * op_it; op_it = new_close_item(); op_it->type = c; read_int32(op_it->o.fd); read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_unlink(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; char c = OP_UNLINK; char buff[MAX_STRING]; unlink_item_t * op_it; op_it = new_unlink_item(); op_it->type = c; read_int32(i32); if ( (rv = fread(buff, sizeof(char), i32, f)) != i32 ) { BIN_READ_ERROR_FREE; } else { buff[i32] = 0; strncpy(op_it->o.name, buff, i32+1); } read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_lseek(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; int64_t i64; char c = OP_LSEEK; lseek_item_t * op_it; op_it = new_lseek_item(); op_it->type = c; read_int32(op_it->o.fd); read_int32(op_it->o.flag); read_int64(op_it->o.offset); read_int64(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_llseek(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; int64_t i64; char c = OP_LLSEEK; llseek_item_t * op_it; op_it = new_llseek_item(); op_it->type = c; read_int32(op_it->o.fd); read_int64(op_it->o.offset); read_int64(op_it->o.f_offset); read_int32(op_it->o.flag); read_int64(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_clone(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; char c = OP_CLONE; clone_item_t * op_it; op_it = new_clone_item(); op_it->type = c; read_int32(op_it->o.mode); read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_mkdir(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; char c = OP_MKDIR; char buff[MAX_STRING]; mkdir_item_t * op_it; op_it = new_mkdir_item(); op_it->type = c; read_int32(i32); if ( (rv = fread(buff, sizeof(char), i32, f)) != i32 ) { BIN_READ_ERROR_FREE; } else { buff[i32] = 0; strncpy(op_it->o.name, buff, i32+1); } read_int32(op_it->o.mode); read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_rmdir(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; char c = OP_MKDIR; char buff[MAX_STRING]; rmdir_item_t * op_it; op_it = new_rmdir_item(); op_it->type = c; read_int32(i32); if ( (rv = fread(buff, sizeof(char), i32, f)) != i32 ) { BIN_READ_ERROR_FREE; } else { buff[i32] = 0; strncpy(op_it->o.name, buff, i32+1); } read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_dup(FILE * f, char c, list_t * list, int64_t num) { int rv; int32_t i32; dup_item_t * op_it; op_it = new_dup_item(); op_it->type = c; read_int32(op_it->o.new_fd); read_int32(op_it->o.old_fd); read_int32(op_it->o.flags); read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_pipe(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; char c = OP_PIPE; pipe_item_t * op_it; op_it = new_pipe_item(); op_it->type = c; read_int32(op_it->o.fd1); read_int32(op_it->o.fd2); read_int32(op_it->o.mode); read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_access(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; char c = OP_ACCESS; char buff[MAX_STRING]; access_item_t * op_it; op_it = new_access_item(); op_it->type = c; read_int32(i32); if ( (rv = fread(buff, sizeof(char), i32, f)) != i32 ) { BIN_READ_ERROR_FREE; } else { buff[i32] = 0; strncpy(op_it->o.name, buff, i32+1); } read_int32(op_it->o.mode); read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_stat(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; char c = OP_STAT; char buff[MAX_STRING]; stat_item_t * op_it; op_it = new_stat_item(); op_it->type = c; read_int32(i32); if ( (rv = fread(buff, sizeof(char), i32, f)) != i32 ) { BIN_READ_ERROR_FREE; } else { buff[i32] = 0; strncpy(op_it->o.name, buff, i32+1); } read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_socket(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; char c = OP_SOCKET; socket_item_t * op_it; op_it = new_socket_item(); op_it->type = c; read_int32(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_read_sendfile(FILE * f, list_t * list, int64_t num) { int rv; int32_t i32; int64_t i64; char c = OP_SENDFILE; sendfile_item_t * op_it; op_it = new_sendfile_item(); op_it->type = c; read_int32(op_it->o.out_fd); read_int32(op_it->o.in_fd); read_int64(op_it->o.offset); read_int64(op_it->o.size); read_int64(op_it->o.retval); if ( (rv = bin_read_info(f, &op_it->o.info, c, num)) != 0) { BIN_READ_ERROR_FREE; } list_append(list, &op_it->item); return 0; } int bin_get_items(char * filename, list_t * list) { FILE * f; char c; long long i = 0; if ((f = fopen(filename, "rb")) == NULL ) { ERRORPRINTF("Error opening file %s: %s\n", filename, strerror(errno)); return errno; } while((c = getc(f)) != EOF) { i++; switch (c) { case OP_WRITE: if ( bin_read_write(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_READ: if ( bin_read_read(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_PWRITE: if ( bin_read_pwrite(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_PREAD: if ( bin_read_pread(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_OPEN: if ( bin_read_open(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_CLOSE: if ( bin_read_close(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_UNLINK: if ( bin_read_unlink(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_LSEEK: if ( bin_read_lseek(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_LLSEEK: if ( bin_read_llseek(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_CLONE: if ( bin_read_clone(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_MKDIR: if ( bin_read_mkdir(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_RMDIR: if ( bin_read_rmdir(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_DUP: case OP_DUP2: case OP_DUP3: if ( bin_read_dup(f, c, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_PIPE: if ( bin_read_pipe(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_ACCESS: if ( bin_read_access(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_STAT: if ( bin_read_stat(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_SOCKET: if ( bin_read_socket(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; case OP_SENDFILE: if ( bin_read_sendfile(f, list, i) != 0 ) { ERRORPRINTF("Error reading binary file: %s\n", filename); return -1; } break; default: ERRORPRINTF("Unknown operation identifier: '%c' reading item no %lld at filepos:%ld\n", c, i, ftell(f)); return -1; break; } } fclose(f); return 0; } /////////////////////////////// // Item saving: /////////////////////////////// inline int bin_write_info(FILE *f, op_info_t * info) { int32_t i32; int rv; write_int32(info->pid); write_int32(info->dur); write_int32(info->start.tv_sec); write_int32(info->start.tv_usec); return 0; } int bin_save_write(FILE * f, write_op_t * op_it) { int rv; int32_t i32; int64_t i64; char c = OP_WRITE; write_char(c); write_int32(op_it->fd); write_int64(op_it->size); write_int64(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_read(FILE * f, read_op_t * op_it) { int rv; int32_t i32; int64_t i64; char c = OP_READ; write_char(c); write_int32(op_it->fd); write_int64(op_it->size); write_int64(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_pwrite(FILE * f, pwrite_op_t * op_it) { int rv; int32_t i32; int64_t i64; char c = OP_PWRITE; write_char(c); write_int32(op_it->fd); write_int64(op_it->size); write_int64(op_it->offset); write_int64(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_pread(FILE * f, pread_op_t * op_it) { int rv; int32_t i32; int64_t i64; char c = OP_PREAD; write_char(c); write_int32(op_it->fd); write_int64(op_it->size); write_int64(op_it->offset); write_int64(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_open(FILE * f, open_op_t * op_it) { int rv; int32_t i32; int32_t len; char c = OP_OPEN; write_char(c); len = strlen(op_it->name); write_int32(len); write_string(op_it->name, len); write_int32(op_it->flags); write_int32(op_it->mode); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_close(FILE * f, close_op_t * op_it) { int rv; int32_t i32; char c = OP_CLOSE; write_char(c); write_int32(op_it->fd); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_unlink(FILE * f, unlink_op_t * op_it) { int rv; int32_t i32; int32_t len; char c = OP_UNLINK; write_char(c); len = strlen(op_it->name); write_int32(len); write_string(op_it->name, len); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_lseek(FILE * f, lseek_op_t * op_it) { int rv; int32_t i32; int64_t i64; char c = OP_LSEEK; write_char(c); write_int32(op_it->fd); write_int32(op_it->flag); write_int64(op_it->offset); write_int64(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_llseek(FILE * f, llseek_op_t * op_it) { int rv; int32_t i32; int64_t i64; char c = OP_LLSEEK; write_char(c); write_int32(op_it->fd); write_int64(op_it->offset); write_int64(op_it->f_offset); write_int32(op_it->flag); write_int64(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_clone(FILE * f, clone_op_t * op_it) { int rv; int32_t i32; char c = OP_CLONE; write_char(c); write_int32(op_it->mode); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_dup(FILE * f, char c, dup_op_t * op_it) { int rv; int32_t i32; write_char(c); write_int32(op_it->new_fd); write_int32(op_it->old_fd); write_int32(op_it->flags); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_mkdir(FILE * f, mkdir_op_t * op_it) { int rv; int32_t i32; int32_t len; char c = OP_MKDIR; write_char(c); len = strlen(op_it->name); write_int32(len); write_string(op_it->name, len); write_int32(op_it->mode); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_rmdir(FILE * f, rmdir_op_t * op_it) { int rv; int32_t i32; int32_t len; char c = OP_MKDIR; write_char(c); len = strlen(op_it->name); write_int32(len); write_string(op_it->name, len); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_pipe(FILE * f, pipe_op_t * op_it) { int rv; int32_t i32; char c = OP_PIPE; write_char(c); write_int32(op_it->fd1); write_int32(op_it->fd2); write_int32(op_it->mode); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_access(FILE * f, access_op_t * op_it) { int rv; int32_t i32; int32_t len; char c = OP_ACCESS; write_char(c); len = strlen(op_it->name); write_int32(len); write_string(op_it->name, len); write_int32(op_it->mode); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_stat(FILE * f, stat_op_t * op_it) { int rv; int32_t i32; int32_t len; char c = OP_STAT; write_char(c); len = strlen(op_it->name); write_int32(len); write_string(op_it->name, len); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_socket(FILE * f, socket_op_t * op_it) { int rv; int32_t i32; char c = OP_SOCKET; write_char(c); write_int32(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_sendfile(FILE * f, sendfile_op_t * op_it) { int rv; int32_t i32; int64_t i64; char c = OP_SENDFILE; write_char(c); write_int32(op_it->out_fd); write_int32(op_it->in_fd); write_int64(op_it->offset); write_int64(op_it->size); write_int64(op_it->retval); if ( (rv = bin_write_info(f, &op_it->info)) != 0) { BIN_WRITE_ERROR; } return 0; } int bin_save_items(char * filename, list_t * list) { FILE * f; long long i = 0; item_t * item = list->head; common_op_item_t * com_it; write_item_t * write_it; read_item_t * read_it; pwrite_item_t * pwrite_it; pread_item_t * pread_it; open_item_t * open_it; close_item_t * close_it; unlink_item_t * unlink_it; lseek_item_t * lseek_it; llseek_item_t * llseek_it; clone_item_t * clone_it; dup_item_t * dup_it; mkdir_item_t * mkdir_it; rmdir_item_t * rmdir_it; pipe_item_t * pipe_it; access_item_t * access_it; stat_item_t * stat_it; socket_item_t * socket_it; sendfile_item_t * sendfile_it; if ((f = fopen(filename, "wb")) == NULL ) { ERRORPRINTF("Error opening file %s: %s\n", filename, strerror(errno)); return errno; } while (item) { i++; com_it = list_entry(item, common_op_item_t, item); switch (com_it->type) { case OP_WRITE: write_it = (write_item_t *) com_it; if ( bin_save_write(f, &write_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_READ: read_it = (read_item_t *) com_it; if ( bin_save_read(f, &read_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_PWRITE: pwrite_it = (pwrite_item_t *) com_it; if ( bin_save_pwrite(f, &pwrite_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_PREAD: pread_it = (pread_item_t *) com_it; if ( bin_save_pread(f, &pread_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_OPEN: open_it = (open_item_t *) com_it; if ( bin_save_open(f, &open_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_CLOSE: close_it = (close_item_t *) com_it; if ( bin_save_close(f, &close_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_UNLINK: unlink_it = (unlink_item_t *) com_it; if ( bin_save_unlink(f, &unlink_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_LSEEK: lseek_it = (lseek_item_t *) com_it; if ( bin_save_lseek(f, &lseek_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_LLSEEK: llseek_it = (llseek_item_t *) com_it; if ( bin_save_llseek(f, &llseek_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_CLONE: clone_it = (clone_item_t *) com_it; if ( bin_save_clone(f, &clone_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_MKDIR: mkdir_it = (mkdir_item_t *) com_it; if ( bin_save_mkdir(f, &mkdir_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_RMDIR: rmdir_it = (rmdir_item_t *) com_it; if ( bin_save_rmdir(f, &rmdir_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_DUP: case OP_DUP2: case OP_DUP3: dup_it = (dup_item_t *) com_it; if ( bin_save_dup(f, com_it->type, &dup_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_PIPE: pipe_it = (pipe_item_t *) com_it; if ( bin_save_pipe(f, &pipe_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_ACCESS: access_it = (access_item_t *) com_it; if ( bin_save_access(f, &access_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_STAT: stat_it = (stat_item_t *) com_it; if ( bin_save_stat(f, &stat_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_SOCKET: socket_it = (socket_item_t *) com_it; if ( bin_save_socket(f, &socket_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; case OP_SENDFILE: sendfile_it = (sendfile_item_t *) com_it; if ( bin_save_sendfile(f, &sendfile_it->o) != 0 ) { ERRORPRINTF("Error saving to binary file %s\n", filename); return -1; } break; default: ERRORPRINTF("Unknown operation identifier: '%c'\n", com_it->type); return -1; break; } item = item->next; } fclose(f); return 0; } ioapps-1.4-r2/replicate.c0000644000175000017500000013045112133726077014710 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "replicate.h" #include "fdmap.h" #include "namemap.h" #include "in_common.h" #include "simulate.h" #include "adt/hash_table.h" #define TIMEVAL_DIFF(t1, t2) (((uint64_t)(t1.tv_sec) * 1000000 + (uint64_t)(t1.tv_usec)) - ((uint64_t)(t2.tv_sec) * 1000000 + (uint64_t)(t2.tv_usec))) #define CALL_TIME(x) ((uint64_t)(x->o.info.start.tv_sec) * 1000000 + (uint64_t)(x->o.info.start.tv_usec)) #define DUR_TIME(x) ((uint32_t)(x->o.info.dur)) extern hash_table_operations_t ht_ops_fdusage; extern hash_table_operations_t ht_ops_fdmapping; char data_buffer[MAX_DATA]; hash_table_t * fd_mappings; /** list (actually a hashtable) of hashtables - one hashtable for each process id. The hashtable for process is used for * mappings of file descriptors recorded --> actually used. */ hash_table_t * usage_map; /** hashtables of fds which are used by this process - to track * whether we can really close fd or it is used by another "cloned process" */ int32_t global_parent_pid = 0; int global_fix_missing = 1; /** whether to try to fix missing clone/open calls in trace */ int global_devnull_fd = 0; int global_devzero_fd = 0; #ifndef PY_MODULE extern struct timeval global_start; #endif unsigned long long clock_rate = 0; struct timeval start_time; static inline unsigned long long int rdtsc(void) { unsigned a, d; __asm__ volatile("rdtsc" : "=a" (a), "=d" (d)); return ((unsigned long long)a) | (((unsigned long long)d) << 32);; } unsigned long long get_clock_rate() { unsigned long long x1; unsigned long long x2; struct timeval tv1, tv2; gettimeofday(&tv1, NULL); x1 = rdtsc(); sleep(1); x2 = rdtsc(); gettimeofday(&tv2, NULL); return (x2-x1)*1000000/TIMEVAL_DIFF(tv2, tv1); } #define REPLICATE(x) x##_it = (x##_item_t *) com_it; \ if ( i==1 ) /*first thing on the list!*/ {\ if( replicate_init(x##_it->o.info.pid, cpu, ifilename, mfilename))\ return -1; \ last_call_orig = CALL_TIME(x##_it); \ first_call_orig = CALL_TIME(x##_it); \ counter_first = rdtsc(); \ counter_last = rdtsc(); \ }\ /** wait for delivering of next call, if enabled */\ if ( op_mask & TIME_DIFF ) { \ diff_orig = CALL_TIME(x##_it) - last_call_orig; \ diff_orig = diff_orig * scale; \ counter_real = rdtsc(); \ diff_real = ((counter_real - counter_last)*1000000)/(clock_rate); \ diff = diff_orig - diff_real; \ /* fprintf(stderr, "orig:%"PRIi64", counter_diff: %"PRIu64", real: %"PRIi64" , diff:%"PRIi64"\n", diff_orig, (counter_real - counter_last), diff_real, diff); */\ while (diff > 60) { \ int y = 0; \ for(y= 0; y < 20000;) { \ y+=1; \ } \ counter_real = rdtsc(); \ diff_real = ((counter_real - counter_last)*1000000)/(clock_rate); \ diff = diff_orig - diff_real; \ } \ } else if ( op_mask & TIME_EXACT ) { \ diff_orig = CALL_TIME(x##_it) - first_call_orig; \ counter_real = rdtsc(); \ diff_real = ((counter_real - counter_first)*1000000)/(clock_rate); \ diff = diff_orig - diff_real; \ while (diff > 60) { \ int y = 0; \ for(y= 0; y < 20000;) { \ y+=1; \ } \ counter_real = rdtsc(); \ diff_real = ((counter_real - counter_first)*1000000)/(clock_rate); \ diff = diff_orig - diff_real; \ } \ \ }\ replicate_##x(x##_it, op_mask);\ if ( op_mask & TIME_DIFF ) { \ last_call_orig = CALL_TIME(x##_it) + DUR_TIME(x##_it); \ counter_last = rdtsc(); \ } \ hash_table_t * replicate_missing_ht(int32_t pid, int op_mask) { if ( ! global_fix_missing ) { ERRORPRINTF("HT for pid %d doesn't exist!\n", pid); return NULL; } else { clone_item_t * op_it = new_clone_item(); op_it->o.retval = pid; op_it->o.info.pid = global_parent_pid; op_it->o.mode = CLONE_FILES; replicate_clone(op_it, op_mask); free(op_it); return get_process_ht(fd_mappings, pid); } } void replicate_get_missing_name(char * buff, int32_t pid, int32_t fd) { static int i =0; sprintf(buff, "UNKNOWN_FILE_%06d_%06d", pid, fd); i++; } item_t * replicate_get_fd_map(hash_table_t * ht, int fd, op_info_t * info, int op_mask) { item_t * fd_map; if ( (fd_map = hash_table_find(ht, &fd)) == NULL) { if ( ! global_fix_missing ) { ERRORPRINTF("%d: Can not find mapping for fd: %d. Corresponding open call probably missing. Time:%d.%d\n", info->pid, fd, info->start.tv_sec, info->start.tv_usec); return NULL; } else { open_item_t * op_it = new_open_item(); op_it->o.retval = fd; op_it->o.info.pid = info->pid; replicate_get_missing_name(op_it->o.name, info->pid, fd); op_it->o.flags = O_RDWR; op_it->o.info.start = info->start; replicate_open(op_it, op_mask); return hash_table_find(ht, &fd); } } else { return fd_map; } } int supported_type(mode_t type) { if ( type == S_IFDIR || type == S_IFREG ) { return 1; } else { return 0; } } /** Replicates one read read(2) call. * @arg op_it operation item structure in which are information about the read(2) call * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_read(read_item_t * op_it, int op_mask) { int64_t retval = 0; int fd = op_it->o.fd; fd_item_t * fd_item; int myfd; item_t * fd_map; char * data; int32_t pid = op_it->o.info.pid; hash_table_t * ht; ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } if ( (fd_map = replicate_get_fd_map(ht, fd, &(op_it->o.info), op_mask)) == NULL) { return; } else { fd_item = hash_table_entry(fd_map, fd_item_t, item); myfd = fd_item->fd_map->my_fd; if ( ! supported_type(fd_item->fd_map->type)) { //DEBUGPRINTF("Unsupported fd (%d -> %d) type: %d\n", fd, myfd, fd_item->fd_map->type); return; } if (op_it->o.size > MAX_DATA) { data = malloc(op_it->o.size); } else { data = data_buffer; } if (op_mask & ACT_SIMULATE) { retval = op_it->o.retval; if (op_it->o.retval != -1) { //do not take unsuccessfull reads into account simulate_read(fd_item, op_it); } } else if ( op_mask & ACT_REPLICATE) { retval = read(myfd, data_buffer, op_it->o.size); } else { assert(0); } fd_item->fd_map->cur_pos += retval; ///< @todo this should be moved to simulate class! if ( op_it->o.size > MAX_DATA) { free(data); } if (retval == -1 && retval != op_it->o.retval) { ERRORPRINTF("%d: Read from fd %d->%d failed: %s\n", pid, fd, myfd, strerror(errno)); // hash_table_dump2(ht, dump_fd_list_item); } else if (retval != op_it->o.size && retval != op_it->o.retval) { DEBUGPRINTF("Warning, %"PRIi64" bytes were successfully read \ (expected: %"PRIi64")\n", retval, op_it->o.retval); } } } /** Replicates one write operation. * @arg op_it operation item structure in which are information about the write operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_write(write_item_t * op_it, int op_mask) { int64_t retval = 0; int fd = op_it->o.fd; fd_item_t * fd_item; int myfd; int32_t pid = op_it->o.info.pid; item_t * fd_map; char * data; hash_table_t * ht; ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } if ( (fd_map = replicate_get_fd_map(ht, fd, &(op_it->o.info), op_mask)) == NULL) { return; } else { fd_item = hash_table_entry(fd_map, fd_item_t, item); myfd = fd_item->fd_map->my_fd; mode_t type = fd_item->fd_map->type; if ( ! supported_type(type)) { //DEBUGPRINTF("Unsupported fd (%d -> %d) type: %d\n", fd, myfd, type); return; } if (op_it->o.size > MAX_DATA) { data = malloc(op_it->o.size); } else { data = data_buffer; } if (op_mask & ACT_SIMULATE) { retval = op_it->o.retval; if (op_it->o.retval != -1) { //do not take unsuccessfull writes into account simulate_write(fd_item, op_it); } } else if ( op_mask & ACT_REPLICATE) { retval = write(myfd, data_buffer, op_it->o.size); } else { assert(0); } fd_item->fd_map->cur_pos += retval; ///< @todo this should be moved to simulate class! if ( op_it->o.size > MAX_DATA) { free(data); } if (retval == -1) { ERRORPRINTF("Write to original fd %d (myfd: %d), name: %s failed: %s\n", fd, myfd, fd_item->fd_map->name, strerror(errno)); } else if (retval != op_it->o.retval) { DEBUGPRINTF("Warning, %"PRIi64" bytes were successfully outputed (%"PRIi64" expected)\n", retval, op_it->o.retval); } } } /** Replicates one pread(2) operation. * @arg op_it operation item structure in which are information about the pread syscall * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_pread(pread_item_t * op_it, int op_mask) { int64_t retval = 0; int fd = op_it->o.fd; fd_item_t * fd_item; int myfd; item_t * fd_map; char * data; int32_t pid = op_it->o.info.pid; hash_table_t * ht; ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } if ( (fd_map = replicate_get_fd_map(ht, fd, &(op_it->o.info), op_mask)) == NULL) { } else { fd_item = hash_table_entry(fd_map, fd_item_t, item); myfd = fd_item->fd_map->my_fd; if ( ! supported_type(fd_item->fd_map->type)) { //DEBUGPRINTF("Unsupported fd (%d -> %d) type: %d\n", fd, myfd, fd_item->fd_map->type); return; } if (op_it->o.size > MAX_DATA) { data = malloc(op_it->o.size); } else { data = data_buffer; } if (op_mask & ACT_SIMULATE) { retval = op_it->o.retval; if (op_it->o.retval != -1) { //do not take unsuccessfull reads into account simulate_pread(fd_item, op_it); } } else if (op_mask & ACT_REPLICATE) { retval = pread(myfd, data_buffer, op_it->o.size, op_it->o.offset); } else { assert(0); } if ( op_it->o.size > MAX_DATA) { free(data); } if (retval == -1 && retval != op_it->o.retval) { ERRORPRINTF("%d: Pread from fd %d->%d failed: %s\n", pid, fd, myfd, strerror(errno)); // hash_table_dump2(ht, dump_fd_list_item); } else if (retval != op_it->o.size && retval != op_it->o.retval) { DEBUGPRINTF("Warning, %"PRIi64" bytes were successfully pread \ (expected: %"PRIi64")\n", retval, op_it->o.retval); } } } /** Replicates one pwrite operation. * @arg op_it operation item structure in which are information about the write operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_pwrite(pwrite_item_t * op_it, int op_mask) { int64_t retval = 0; int fd = op_it->o.fd; fd_item_t * fd_item; int myfd; int32_t pid = op_it->o.info.pid; item_t * fd_map; char * data; hash_table_t * ht; ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } if ( (fd_map = replicate_get_fd_map(ht, fd, &(op_it->o.info), op_mask)) == NULL) { } else { fd_item = hash_table_entry(fd_map, fd_item_t, item); myfd = fd_item->fd_map->my_fd; mode_t type = fd_item->fd_map->type; if ( ! supported_type(type)) { //DEBUGPRINTF("Unsupported fd (%d -> %d) type: %d\n", fd, myfd, type); return; } if (op_it->o.size > MAX_DATA) { data = malloc(op_it->o.size); } else { data = data_buffer; } if (op_mask & ACT_SIMULATE) { retval = op_it->o.retval; if (op_it->o.retval != -1) { //do not take unsuccessfull writes into account simulate_pwrite(fd_item, op_it); } } else if ( op_mask & ACT_REPLICATE) { retval = pwrite(myfd, data_buffer, op_it->o.size, op_it->o.offset); } else { assert(0); } if ( op_it->o.size > MAX_DATA) { free(data); } if (retval == -1) { ERRORPRINTF("Pwrite to fd %d failed: %s\n", fd, strerror(errno)); } else if (retval != op_it->o.retval) { DEBUGPRINTF("Warning, %"PRIi64" bytes were successfully outputed (%"PRIi64" expected)\n", retval, op_it->o.retval); } } } inline int32_t get_pipe_fd() { static int32_t fd = INT_MAX; fd--; return fd; } inline int32_t get_socket_fd() { static int32_t fd = 100000000+1; fd--; return fd; } /** Replicates one pipe(2) operation. It actually does not call pipe syscall, but just keep track of fds. * @arg op_it operation item structure in which are information about the open/creat operation */ void replicate_pipe(pipe_item_t * op_it, int op_mask) { int32_t retval = op_it->o.retval; hash_table_t * ht; int32_t pid = op_it->o.info.pid; fd_item_t * fd_item;; int32_t fd1 = op_it->o.fd1; int32_t fd2 = op_it->o.fd2; if (retval == -1) { //original open call failed, don't do anything DEBUGPRINTF("Original pipe(2) call failed, skipping%s", "\n"); return; } ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } if ( hash_table_find(ht, &fd1) == NULL && hash_table_find(ht, &fd2) == NULL ) { //we didn't open any fd before fd_item = new_fd_item(); fd_item->fd_map->my_fd = get_pipe_fd(); fd_item->fd_map->type = S_IFIFO; fd_item->old_fd = fd1; insert_parent_fd(fd_item, fd1); hash_table_insert(ht, &fd1, &fd_item->item); increase_fd_usage(usage_map, fd1); fd_item = new_fd_item(); fd_item->fd_map->my_fd = get_pipe_fd(); fd_item->fd_map->type = S_IFIFO; fd_item->old_fd = fd2; insert_parent_fd(fd_item, fd2); hash_table_insert(ht, &fd2, &fd_item->item); increase_fd_usage(usage_map, fd2); // DEBUGPRINTF("%d: Pipes %d and %d inserted.\n", pid, fd1, fd2); } else { ERRORPRINTF("%d(%d.%d): One of the fds: %d %d already opened!\n", pid, op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, fd1, fd2); } } /** Replicates one open(2)/creat(2) operation. * @arg op_it operation item structure in which are information about the open/creat operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_open(open_item_t * op_it, int op_mask) { int retval; char * name = NULL; int fd = op_it->o.retval; int flags = 0; hash_table_t * ht; int32_t pid = op_it->o.info.pid; fd_item_t * fd_item = new_fd_item(); if (fd == -1) { //original open call failed, just replicate it name = namemap_get_name(op_it->o.name); if ( name == NULL ) { // I should ignore it //DEBUGPRINTF("Ignoring opening of file %s\n", op_it->o.name); return; } if (op_mask & ACT_REPLICATE) { retval = open(name, flags); } else { if (op_mask & ACT_SIMULATE) { if (name != op_it->o.name) { strcpy(op_it->o.name, name); } simulate_creat(&op_it->o); } retval = -1; } if (retval != -1) { ERRORPRINTF("%d: Error replicating originally failed open call with file %s\n", pid, name); close(retval); } delete_fd_item(fd_item); return; } ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { delete_fd_item(fd_item); return; } } flags = op_it->o.flags; name = namemap_get_name(op_it->o.name); if ( name == NULL ) { // I should ignore it //DEBUGPRINTF("Ignoring opening of file %s\n", op_it->o.name); flags |= O_IGNORE; name = op_it->o.name; } if ( hash_table_find(ht, &fd) == NULL ) { //we didn't open this file before if (op_mask & ACT_REPLICATE && ! (flags & O_IGNORE) ) { //i should replicate and not ignore it if (op_it->o.mode == MODE_UNDEF) { //we know, that we don't want to use mode flag at all retval = open(name, flags); } else { retval = open(name, flags, op_it->o.mode); } } else { // ACT_SIMULATE or O_IGNORE if (op_it->o.name != name) { strcpy(op_it->o.name, name); } if (op_mask & ACT_SIMULATE && ! (flags & O_IGNORE)) { simulate_creat(&op_it->o); } retval = simulate_get_open_fd(); } if (retval == -1) { ERRORPRINTF("Open of file %s failed: %s\n", name, strerror(errno)); } else { //everything went OK, lets insert it into our mapping fd_item->fd_map->my_fd = retval; fd_item->fd_map->cur_pos = 0; fd_item->fd_map->time_open = op_it->o.info.start; strncpy(fd_item->fd_map->name, name, MAX_STRING); fd_item->fd_map->name[MAX_STRING-1] = 0; //just to make sure it will be terminated fd_item->old_fd = op_it->o.retval; fd_item->fd_map->created = flags & O_CREAT; insert_parent_fd(fd_item, op_it->o.retval); if ( flags & O_IGNORE ) { fd_item->fd_map->type = S_IFIGNORE; } else if ( flags & O_DIRECTORY ) { fd_item->fd_map->type = S_IFDIR; } else { fd_item->fd_map->type = S_IFREG; } hash_table_insert(ht, &op_it->o.retval, &fd_item->item); increase_fd_usage(usage_map, retval); //DEBUGPRINTF("%d: File %s inserted with key %d->%d\n", pid, name, op_it->o.retval, retval); } } else { if ( flags & O_IGNORE ) { return; } ERRORPRINTF("%d: File %s is already opened!\n", pid, op_it->o.name); delete_fd_item(fd_item); } } /** Replicate clone operation. * @arg op_it operation item structure in which are information about the close operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_clone(clone_item_t * op_it, int op_mask) { //item_t * item; int32_t pid = op_it->o.retval; hash_table_t * ht; //sanity check: if ( (ht = get_process_ht(fd_mappings, pid)) != NULL) { ERRORPRINTF("Table for process %d already exist!\n", pid); return; } item_t * item = new_process_ht(pid); process_hash_item_t * h_it = hash_table_entry(item, process_hash_item_t, item); if (op_it->o.mode & CLONE_FILES) { //we should have the same FD table free(h_it->ht); //we don't want our own ht, because we just want a pointer to the same list h_it->ht = get_process_ht(fd_mappings, op_it->o.info.pid); } else { //we will just copy FD table free(h_it->ht); h_it->ht = duplicate_process_ht(get_process_ht(fd_mappings, op_it->o.info.pid), usage_map); } hash_table_insert(fd_mappings, &h_it->pid, &h_it->item); //hash_table_dump2(h_it->ht, dump_fd_list_item); //list_dump(fd_mappings, dump_process_hash_list_item); } /** Replicates one close operation. * @arg op_it operation item structure in which are information about the close operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_close(close_item_t * op_it, int op_mask) { int retval; int fd = op_it->o.fd; int myfd; item_t * item; fd_item_t * fd_item; fd_map_t * fd_map = NULL; int32_t pid = op_it->o.info.pid; hash_table_t * ht; int last = 0; ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } if ( (item = replicate_get_fd_map(ht, fd, &(op_it->o.info), op_mask)) == NULL) { //we didn't open this file before if ( op_it->o.retval != -1 ) { ERRORPRINTF("%d: File descriptor %d is not opened!\n", pid, fd); } } else { fd_item = list_entry(item, fd_item_t, item); myfd = fd_item->fd_map->my_fd; //Maybe it was just a pipe or socket? if ( ! supported_type(fd_item->fd_map->type)) { retval = 0; } else { if (decrease_fd_usage(usage_map, myfd)) { // it was the last one, really close it if (op_mask & ACT_REPLICATE) { retval = close(myfd); } else { //simulating mode retval = 0; } } else { retval = 0; //don't close it, another "process" have it open } } if (retval == -1) { ERRORPRINTF("%d: Close of file with fd %d->%d failed: %s\n", pid, fd, myfd, strerror(errno)); } else { //everything went OK, lets remove it from the mapping last = delete_parent_fd(fd_item, fd); fd_map = fd_item->fd_map; hash_table_remove(ht, &fd); if (last) { assert(fd_map); free(fd_map); //See comment 4 lines above } // DEBUGPRINTF("%d: Mapping of fd: %d->%d removed\n", pid, fd, myfd); } // hash_table_dump2(ht, dump_fd_list_item); } } /** Replicates one unlink operation. * @arg op_it operation item structure in which are information about the unlink operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_unlink(unlink_item_t * op_it, int op_mask) { int retval; char * name; name = namemap_get_name(op_it->o.name); if ( name == NULL ) { // I should ignore it return; } if ( op_mask & ACT_REPLICATE) { retval = unlink(name); if (retval == -1 && retval != op_it->o.retval) { ERRORPRINTF("Unlink of file with %s failed (which was not expected): %s\n", name, strerror(errno)); } else if (retval != op_it->o.retval) { ERRORPRINTF("Unlink result of file %s other than expected: %d\n", name, retval); } } else if ( op_mask & ACT_SIMULATE ) { simulate_unlink(&op_it->o); } } /** Replicates one _llseek operation. This syscall is apparently only available on x86 kernels, but we simulate * actual seek on 64bit machines too. * * @arg op_it operation item structure in which are information about the _llseek operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_llseek(llseek_item_t * op_it, int op_mask) { int64_t retval; loff_t result = 0; int fd = op_it->o.fd; int myfd; item_t * fd_map; fd_item_t * fd_item; int32_t pid = op_it->o.info.pid; hash_table_t * ht; unsigned long high, low; ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } high = (off_t) (op_it->o.offset>>32); low = (off_t) (op_it->o.offset & 0xFFFFFFFF); if ( (fd_map = replicate_get_fd_map(ht, fd, &(op_it->o.info), op_mask)) == NULL) { ERRORPRINTF("%d: Can not find mapping for fd: %d. Corresponding open call probably missing.\n", pid, fd); } else { fd_item = hash_table_entry(fd_map, fd_item_t, item); myfd = fd_item->fd_map->my_fd; mode_t type = fd_item->fd_map->type; if ( ! supported_type(type)) { // DEBUGPRINTF("%d: Unsupported fd (%d -> %d) type: %d\n", pid, fd, myfd, type); return; } if ( op_mask & ACT_REPLICATE) { #ifdef SYS__llseek //32bit machine retval = syscall(SYS__llseek, myfd, high, low, &result, op_it->o.flag); #else retval = lseek(myfd, op_it->o.offset, op_it->o.flag); result = retval; #endif } else { result = op_it->o.f_offset; retval = op_it->o.retval; } #ifdef SYS__llseek //32bit machine if (retval == -1 && retval != op_it->o.retval) { #else if (retval == (off_t) -1 && retval != op_it->o.retval) { #endif ERRORPRINTF("lseek with time %d.%d of file with %d failed (which was not expected): %s, %d\n", op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.fd, strerror(errno), errno); } else if (result != op_it->o.f_offset) { ERRORPRINTF("_llseek's final offset (%"PRIi64") is different from what expected(%"PRIi64"), time: %d.%d\n", result, op_it->o.f_offset, op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec); if (op_mask & ACT_SIMULATE) { fd_item->fd_map->cur_pos = op_it->o.f_offset; } } else { if (op_mask & ACT_SIMULATE) { fd_item->fd_map->cur_pos = op_it->o.f_offset; } } } } /** Replicates one lseek operation. This call apparently only exists on 64bit systems, but we simulate it * using _llseek on 32bit systems too. * * @arg op_it operation item structure in which are information about the lseek operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_lseek(lseek_item_t * op_it, int op_mask) { #ifdef SYS__llseek //32bit machine, simulate it through _llseek call llseek_item_t * llop_it = new_llseek_item(); memcpy(&(llop_it->o.info), &(op_it->o.info), sizeof(op_info_t)); llop_it->o.fd = op_it->o.fd; llop_it->o.offset = op_it->o.offset; llop_it->o.flag = op_it->o.flag; llop_it->o.f_offset = op_it->o.retval; llop_it->o.retval = op_it->o.retval; replicate_llseek(llop_it, op_mask); free(llop_it); #else int64_t retval; int32_t fd = op_it->o.fd; int32_t myfd; item_t * fd_map; fd_item_t * fd_item; int32_t pid = op_it->o.info.pid; hash_table_t * ht; ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } if ( (fd_map = replicate_get_fd_map(ht, fd, &(op_it->o.info), op_mask)) == NULL) { ERRORPRINTF("%d: Can not find mapping for fd: %d. Corresponding open call probably missing.\n", pid, fd); } else { fd_item = hash_table_entry(fd_map, fd_item_t, item); myfd = fd_item->fd_map->my_fd; mode_t type = fd_item->fd_map->type; if ( ! supported_type(type)) { // DEBUGPRINTF("%d: Unsupported fd (%d -> %d) type: %d\n", pid, fd, myfd, type); return; } if ( op_mask & ACT_REPLICATE) { retval = lseek(myfd, op_it->o.offset, op_it->o.flag); } else { retval = op_it->o.retval; } if (retval == (off_t) -1 && retval != op_it->o.retval) { ERRORPRINTF("lseek with time %d.%d of file with %d failed (which was not expected): %s\n", op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.fd, strerror(errno)); } else if (retval != op_it->o.retval) { ERRORPRINTF("lseek's final offset (%"PRIi64") is different from what expected(%"PRIi64")\n", retval, op_it->o.retval); if (op_mask & ACT_SIMULATE) { fd_item->fd_map->cur_pos = retval; } } else { if (op_mask & ACT_SIMULATE) { fd_item->fd_map->cur_pos = retval; } } } #endif } /** Replicates one sendfile operation. * * @arg op_it operation item structure in which are information about the sendfile operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_sendfile(sendfile_item_t * op_it, int op_mask) { int64_t retval = 0; int32_t out_fd = op_it->o.out_fd; int32_t in_fd = op_it->o.in_fd; int32_t out_myfd; int32_t in_myfd; item_t * out_fd_map; item_t * in_fd_map; fd_item_t * out_fd_item; fd_item_t * in_fd_item; int32_t pid = op_it->o.info.pid; hash_table_t * ht; ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } if ( (out_fd_map = replicate_get_fd_map(ht, out_fd, &(op_it->o.info), op_mask)) == NULL) { return; } else { if ( (in_fd_map = replicate_get_fd_map(ht, in_fd, &(op_it->o.info), op_mask)) == NULL) { return; } out_fd_item = hash_table_entry(out_fd_map, fd_item_t, item); in_fd_item = hash_table_entry(in_fd_map, fd_item_t, item); out_myfd = out_fd_item->fd_map->my_fd; in_myfd = in_fd_item->fd_map->my_fd; mode_t in_type = in_fd_item->fd_map->type; mode_t out_type = out_fd_item->fd_map->type; if ( supported_type(in_type) && supported_type(out_type)) { //both fd types are supported if ( op_mask & ACT_REPLICATE) { retval = sendfile(out_myfd, in_myfd, &op_it->o.offset, op_it->o.size); } else if ( op_mask & ACT_SIMULATE) { if ( op_it->o.retval != -1 ) { simulate_sendfile(in_fd_item, out_fd_item, op_it); } retval = op_it->o.retval; } } else if ( ! supported_type(in_type) && ! supported_type(out_type)) { //none of the types is supported return; } else if ( ! supported_type(in_type)) { //just out is supported (this is a bit strange....) if ( op_mask & ACT_REPLICATE) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23) /* sendfile now supports file-to-file (and not only file-to-socket) operations */ //we simulate it using /dev/zero device retval = sendfile(out_myfd, global_devzero_fd, &op_it->o.offset, op_it->o.size); #else //we can't use sendfile(2), but at least access the disk via write() syscall char * buff = malloc(op_it->o.size); if ( op_it->o.offset == OFFSET_INVAL ) { retval = write(out_myfd, buff, op_it->o.size); } else { retval = pwrite(out_myfd, buff, op_it->o.size, op_it->o.offset); } free(buff); #endif } else if ( op_mask & ACT_SIMULATE) { if ( op_it->o.retval != -1 ) { simulate_sendfile(NULL, out_fd_item, op_it); } retval = op_it->o.retval; } } else if ( ! supported_type(out_type)) { //just in is supported (the most likely case) if ( op_mask & ACT_REPLICATE) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,23) /* sendfile now supports file-to-file (and not only file-to-socket) operations */ //we simulate it using /dev/null device retval = sendfile(global_devnull_fd, in_myfd, &op_it->o.offset, op_it->o.size); #else //we can't use sendfile(2), but at least access the disk via read() syscall char * buff = malloc(op_it->o.size); if ( op_it->o.offset == OFFSET_INVAL ) { retval = read(in_myfd, buff, op_it->o.size); } else { retval = pread(in_myfd, buff, op_it->o.size, op_it->o.offset); } free(buff); #endif } else if ( op_mask & ACT_SIMULATE) { if ( op_it->o.retval != -1 ) { simulate_sendfile(in_fd_item, NULL, op_it); } retval = op_it->o.retval; } } //check retval if (retval == -1 && retval != op_it->o.retval && ! supported_type(in_type)) { ERRORPRINTF("sendfile with time %d.%d from %d (myfd: %d) to %d (myfd: %d) failed (which was not expected): %s\n", op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.in_fd, global_devzero_fd, op_it->o.out_fd, out_myfd, strerror(errno)); } else if (retval == -1 && retval != op_it->o.retval && ! supported_type(out_type)) { ERRORPRINTF("sendfile with time %d.%d from %d (myfd: %d) to %d (myfd: %d) failed (which was not expected): %s\n", op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.in_fd, in_myfd, op_it->o.out_fd, global_devnull_fd, strerror(errno)); } else if (retval == -1 && retval != op_it->o.retval) { ERRORPRINTF("sendfile with time %d.%d from %d (myfd: %d) to %d (myfd: %d) failed (which was not expected): %s\n", op_it->o.info.start.tv_sec, op_it->o.info.start.tv_usec, op_it->o.in_fd, in_myfd, op_it->o.out_fd, out_myfd, strerror(errno)); } else if (retval != op_it->o.retval) { ERRORPRINTF("sendfile's retval (%"PRIi64") is different from what expected(%"PRIi64")\n", retval, op_it->o.retval); if ( op_it->o.offset == OFFSET_INVAL ) { //i.e. NULL pointer value, offset is updated in_fd_item->fd_map->cur_pos += retval; } out_fd_item->fd_map->cur_pos += retval; } else { if ( op_it->o.offset == OFFSET_INVAL ) { //i.e. NULL pointer value, offset is updated in_fd_item->fd_map->cur_pos += op_it->o.size; } out_fd_item->fd_map->cur_pos += retval; } return; } } /** Replicates one mkdir operation. * @arg op_it operation item structure in which are information about the _llseek operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_mkdir(mkdir_item_t * op_it, int op_mask) { int retval; char * name; name = namemap_get_name(op_it->o.name); if ( name == NULL ) { // I should ignore it return; } if (op_mask & ACT_REPLICATE) { retval = mkdir(name, op_it->o.mode); if (retval == -1 && retval != op_it->o.retval) { ERRORPRINTF("Mkdir of file with %s failed (which was not expected): %s\n", name, strerror(errno)); } else if (retval != op_it->o.retval) { ERRORPRINTF("Mkdir result of file %s other than expected: %d\n", name, retval); } } else if ( op_mask & ACT_SIMULATE ) { simulate_mkdir(&op_it->o); } } /** Replicates one rmdir operation. * @arg op_it operation item structure in which are information about the _llseek operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_rmdir(rmdir_item_t * op_it, int op_mask) { int retval; char * name; name = namemap_get_name(op_it->o.name); if ( name == NULL ) { // I should ignore it return; } if (op_mask & ACT_REPLICATE) { retval = rmdir(name); if (retval == -1 && retval != op_it->o.retval) { ERRORPRINTF("Rmdir of file with %s failed (which was not expected): %s\n", name, strerror(errno)); } else if (retval != op_it->o.retval) { ERRORPRINTF("Rmdir result of file %s other than expected: %d\n", name, retval); } } else if ( op_mask & ACT_SIMULATE ) { simulate_rmdir(&op_it->o); } } /** Replicates one dup operation. It actually don't call the operation itself, but only * keeps track of what it did in original process . * * @arg op_it operation item structure in which are information about the _llseek operation * @arg op_mask whether really replicate or just simulate it. ACT_SIMULATE or ACT_REPLICATE. */ void replicate_dup(dup_item_t * op_it, int op_mask) { int32_t pid = op_it->o.info.pid; hash_table_t * ht; fd_item_t * fd_item; fd_item_t * fd_item_new; item_t * it; int fd = op_it->o.old_fd; int new_fd = op_it->o.retval; if (op_it->o.retval == -1) { //dup call failed, which was not expected DEBUGPRINTF("Previous duplicate call failed, doing nothing%s", "\n"); return; } ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } if ( (it = replicate_get_fd_map(ht, fd, &(op_it->o.info), op_mask)) == NULL) { ERRORPRINTF("Can not find mapping for fd: %d. Corresponding open call probably missing.\n", fd); } else { fd_item = hash_table_entry(it, fd_item_t, item); fd_item_new = new_fd_item(); fd_item_new->old_fd = new_fd; free(fd_item_new->fd_map); // we will use the same fd_map as previous fd, because we are just dupl. fd_item_new->fd_map = fd_item->fd_map; insert_parent_fd(fd_item, new_fd); // DEBUGPRINTF("%d: Duplicating fd %d->%d to %d->%d.\n", pid, fd, fd_item->fd_map->my_fd, new_fd, // fd_item->fd_map->my_fd); hash_table_insert(ht, &new_fd, &fd_item_new->item); increase_fd_usage(usage_map, fd_item_new->fd_map->my_fd); } } /** Replicates one access operation. * @arg op_it operation item structure in which are information about the _llseek operation */ void replicate_access(access_item_t * op_it, int op_mask) { int retval; char * name; name = namemap_get_name(op_it->o.name); if ( name == NULL ) { // I should ignore it return; } else { if (name != op_it->o.name) { strcpy(op_it->o.name, name); } } if (op_mask & ACT_REPLICATE) { retval = access(op_it->o.name, op_it->o.mode); if (retval == -1 && retval != op_it->o.retval) { ERRORPRINTF("Access of file with %s failed (which was not expected): %s\n", op_it->o.name, strerror(errno)); } else if (retval != op_it->o.retval) { ERRORPRINTF("Access result of file %s other than expected: %d\n", op_it->o.name, retval); } } else if (op_mask & ACT_SIMULATE) { simulate_access(&op_it->o); } } /** Replicates one stat operation. * @arg op_it operation item structure in which are information about the _llseek operation */ void replicate_stat(stat_item_t * op_it, int op_mask) { int retval; char * name; struct stat st_buf; name = namemap_get_name(op_it->o.name); if ( name == NULL ) { // I should ignore it return; } else { if (name != op_it->o.name) { strcpy(op_it->o.name, name); } } if (op_mask & ACT_REPLICATE) { retval = stat(op_it->o.name, &st_buf); if (retval == -1 && retval != op_it->o.retval) { ERRORPRINTF("Stat on file with %s failed (which was not expected): %s\n", op_it->o.name, strerror(errno)); } else if (retval != op_it->o.retval) { ERRORPRINTF("Stat result of file %s other than expected: %d\n", op_it->o.name, retval); } } else if (op_mask & ACT_SIMULATE) { simulate_stat(&op_it->o); } } /** Replicates one socket(2) operation. It just keep tracks track of fd_mapping, no actual socket * calls are performed. * * @arg op_it operation item structure in which are information about the open/creat operation */ void replicate_socket(socket_item_t * op_it, int op_mask) { int retval; int fd = op_it->o.retval; hash_table_t * ht; int32_t pid = op_it->o.info.pid; if (fd == -1) { //original scoket call failed, just exit retval = -1; return; } ht = get_process_ht(fd_mappings, pid); if (! ht) { ht = replicate_missing_ht(pid, op_mask); if (! ht) { return; } } if ( hash_table_find(ht, &fd) == NULL ) { //we didn't open this file before fd_item_t * fd_item = new_fd_item(); retval = get_socket_fd(); fd_item->fd_map->my_fd = retval; fd_item->old_fd = fd; fd_item->fd_map->time_open = op_it->o.info.start; fd_item->fd_map->name[MAX_STRING-1] = 0; //just to make sure it will be terminated fd_item->fd_map->type = S_IFSOCK; insert_parent_fd(fd_item, op_it->o.retval); hash_table_insert(ht, &fd, &fd_item->item); increase_fd_usage(usage_map, retval); // DEBUGPRINTF("%d: Socket inserted with key %d->%d\n", pid, fd, retval); // hash_table_dump2(ht, dump_fd_list_item); } else { ERRORPRINTF("%d: Fd %d is already opened!\n", pid, fd); } } /** This functions initialize all structures that are needed. It also creates initial mappings of FDs such as * stdin, stdout and stderr. * * @args fd_mappings list of hash tables of processes * @arg pid pid of the main process */ int replicate_init(int32_t pid, int cpu, char * ifilename, char * mfilename) { fd_item_t * fd_item; hash_table_t * ht; int i; #ifndef PY_MODULE /* Make sure we are bounded only to a particular processor. This is important when using program counter as they differ per processor */ cpu_set_t mask;; /* processors to bind */ CPU_ZERO(&mask); CPU_SET(cpu, &mask); unsigned int len = sizeof(mask); if (sched_setaffinity(0, len, (cpu_set_t *)&mask) < 0) { perror("sched_setaffinity"); } #endif //prepare list of files to ignores + list of mapped files if ( namemap_init(ifilename, mfilename) ) { return -1; } fd_mappings = malloc(sizeof(hash_table_t)); usage_map = malloc(sizeof(hash_table_t)); //init fd_mappings hash_table_init(fd_mappings, HASH_TABLE_SIZE, &ht_ops_fdmapping); //Init usage_map hash_table_init(usage_map, HASH_TABLE_SIZE, &ht_ops_fdusage); //create a new ht for the process DEBUGPRINTF("Initializing with pid %d\n", pid); global_parent_pid = pid; item_t * item = new_process_ht(pid); process_hash_item_t * h_it = hash_table_entry(item, process_hash_item_t, item); hash_table_insert(fd_mappings, &h_it->pid, &h_it->item); ht = h_it->ht; fd_item = new_fd_item(); fd_item->old_fd = STDIN_FILENO; fd_item->fd_map->my_fd = STDIN_FILENO; strncpy(fd_item->fd_map->name, "stdin", MAX_STRING); fd_item->fd_map->type = FT_SPEC; // in reality, this should by S_IFREG, but we need some special handling insert_parent_fd(fd_item, STDIN_FILENO); i = STDOUT_FILENO; hash_table_insert(ht, &i, &fd_item->item); increase_fd_usage(usage_map,i); fd_item = new_fd_item(); fd_item->old_fd = STDOUT_FILENO; fd_item->fd_map->my_fd = STDOUT_FILENO; strncpy(fd_item->fd_map->name, "stdout", MAX_STRING); fd_item->fd_map->type = FT_SPEC; // in reality, this should by S_IFREG, but we need some special handling fd_item->fd_map->type = FT_SPEC; // in reality, this should by S_IFREG, but we need some special handling insert_parent_fd(fd_item, STDOUT_FILENO); i = STDOUT_FILENO; hash_table_insert(ht, &i, &fd_item->item); increase_fd_usage(usage_map,i); fd_item = new_fd_item(); fd_item->old_fd = STDERR_FILENO; fd_item->fd_map->my_fd = STDERR_FILENO; strncpy(fd_item->fd_map->name, "stderr", MAX_STRING); fd_item->fd_map->type = FT_SPEC; // in reality, this should by S_IFREG, but we need some special handling insert_parent_fd(fd_item, STDERR_FILENO); i = STDERR_FILENO; hash_table_insert(ht, &i, &fd_item->item); increase_fd_usage(usage_map,i); DEBUGPRINTF("Initialized%s", "\n"); #ifndef PY_MODULE gettimeofday(&start_time, NULL); DEBUGPRINTF("Time elapsed so far: %lf\n", TIMEVAL_DIFF(start_time, global_start)/1000000.0); #endif global_devnull_fd = open("/dev/null", O_WRONLY); if ( global_devnull_fd == -1 ) { ERRORPRINTF("Error opening /dev/null: %s", strerror(errno)); return -1; } global_devzero_fd = open("/dev/zero", O_RDONLY); if ( global_devnull_fd == -1 ) { ERRORPRINTF("Error opening /dev/zero: %s", strerror(errno)); return -1; } return 0; } /** Dealocates all support structures used by replicate. */ void replicate_finish() { // item_t * i; // process_hash_item_t * process_ht_item; #ifndef PY_MODULE struct timeval cur_time; gettimeofday(&cur_time, NULL); DEBUGPRINTF("The replication itself lasted for %lf\n", TIMEVAL_DIFF(cur_time, start_time)/(1000000.0)); fprintf(stdout, "Result: %lf\n", TIMEVAL_DIFF(cur_time, start_time)/(1000000.0)); #endif namemap_finish(); ///< @todo get rid of process_map_hts & fd_maps & fd_mappings hashmap. This is tricky, as they are shared across processes, // so one can't just iterate throw them and blindly delete them // hash_table_apply(process_ht_item->ht, fd_item_remove_fd_map); // hash_table_destroy(process_ht_item->ht); // free(process_ht_item->ht); // free(process_ht_item); // } hash_table_destroy(usage_map); // free(fd_mappings); free(usage_map); } /** This function replicates every file operation in the @a list. * @arg list list of operations to replicate * @arg cpu cpu number to bind this process. * @arg scale factor by which to scale time window between calls in TIME_DIFF mode * @arg op_mask mode of replication, it can only simulate replication or really duplicate. * This also affects timing behaviour. * @arg ifile name of the file containing file names to ignore. NULL to disable this feature. * @arg mfile name of the file containing mapping of file names. Operation will be performed * on mapped file instead of the recorded one. NULL to disable this feature. * * @return zero if succesfull, non-zero otherwise * */ int replicate(list_t * list, int cpu, double scale, int op_mask, char * ifilename, char * mfilename) { long long i = 0; item_t * item = list->head; common_op_item_t * com_it; read_item_t * read_it; write_item_t * write_it; pread_item_t * pread_it; pwrite_item_t * pwrite_it; open_item_t * open_it; close_item_t * close_it; unlink_item_t * unlink_it; llseek_item_t * llseek_it; lseek_item_t * lseek_it; clone_item_t * clone_it; dup_item_t * dup_it; mkdir_item_t * mkdir_it; rmdir_item_t * rmdir_it; pipe_item_t * pipe_it; access_item_t * access_it; stat_item_t * stat_it; socket_item_t * socket_it; sendfile_item_t * sendfile_it; uint64_t last_call_orig; ///< when was the last original call made uint64_t first_call_orig; ///< when was the first original call made int64_t diff_orig, diff_real; int64_t diff; uint64_t counter_first, counter_last, counter_real; if ( ! (op_mask & FIX_MISSING) ) { global_fix_missing = 0; } if ( (op_mask & ACT_REPLICATE) && ! (op_mask & TIME_ASAP) ) { fprintf(stderr, "Determining clock rate.."); clock_rate = get_clock_rate(); fprintf(stderr, ": %lfMHz\n", (double)(clock_rate)/1000000.0); } while (item) { i++; com_it = list_entry(item, common_op_item_t, item); switch (com_it->type) { case OP_WRITE: REPLICATE(write); break; case OP_READ: REPLICATE(read); break; case OP_PWRITE: REPLICATE(pwrite); break; case OP_PREAD: REPLICATE(pread); break; case OP_OPEN: REPLICATE(open); break; case OP_CLOSE: REPLICATE(close); break; case OP_UNLINK: REPLICATE(unlink); break; case OP_LSEEK: REPLICATE(lseek); break; case OP_LLSEEK: REPLICATE(llseek); break; case OP_CLONE: REPLICATE(clone); break; case OP_MKDIR: REPLICATE(mkdir); break; case OP_RMDIR: REPLICATE(rmdir); break; case OP_DUP: case OP_DUP2: case OP_DUP3: REPLICATE(dup); break; case OP_PIPE: REPLICATE(pipe); break; case OP_ACCESS: REPLICATE(access); break; case OP_STAT: REPLICATE(stat); break; case OP_SOCKET: REPLICATE(socket); break; case OP_SENDFILE: REPLICATE(sendfile); break; default: ERRORPRINTF("Unknown operation identifier: '%c'\n", com_it->type); return -1; break; } item = item->next; } replicate_finish(); return 0; } ioapps-1.4-r2/in_strace.c0000644000175000017500000011404012133726077014703 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "in_strace.h" #include "in_common.h" #include "adt/list.h" #include "stats.h" static int ht_compare_isyscall(key_t *key, item_t *item) { isyscall_t * isyscall; isyscall = hash_table_entry(item, isyscall_t, item); return isyscall->pid == *key; } static inline void ht_remove_callback_isyscall(item_t * item) { isyscall_t * isyscall = hash_table_entry(item, isyscall_t, item); free(isyscall); return; } /** hash table operations. */ static hash_table_operations_t ht_ops_isyscall = { .hash = ht_hash_int, .compare = ht_compare_isyscall, .remove_callback = ht_remove_callback_isyscall /* = NULL if not used */ }; /** This function returns position of coma after second quota in the list. This is usefull when parsing * file reads and writes, that was aquired with strace -sX, where X is not zero. * There are four possibilities for what line could look like: * 1. nothing with dots (if strace was executed with -s0: , ""..., ), * 2. nothing without dots (if read/write was really for 0 bytes , "", ), * 3. <= than X characters, where X was specified as -sX, "xxxxxx" * 4. More than X, resulting string will be "xxxxx"..., * 5. NULL string. That one I don't understand, but it seems it happens from time to time: NULL, * * @arg line line to parse * @return pointer to comma after second quote in the line. */ inline char * strace_pos_comma(char * line) { char *c, *lc; int backslash = 0; c = line; lc = NULL; while (*c && *c != '"' ) { // find first quote lc = c; c++; } if ( ! *c ) { //maybe there was "NULL": if ( (c = strstr(line, "NULL,")) != NULL ) { return c + strlen("NULL,") -1; } else { ERRORPRINTF("Unexpected end of line: %s", line); return NULL; } } if ( lc == NULL ) { ERRORPRINTF("Unexpected end of line: %s", line); return NULL; } c++; while ( *c ) { // find the second quote if ( *c == '"' ) { if (! backslash) { // make sure we spot something like this "read(3, "blabla\"bla", 10) break; } backslash = 0; } else if ( *c == '\\' ) { if (backslash) { //two consecutives backslashes - they do not backslash special character but themselves backslash = 0; } else { backslash = 1; } } else { backslash = 0; } lc = c; c++; } c++; if ( *c ) { if ( *c == '.' ) { while ( *c && *c != ',' ) c++; return c; } else if ( *c == ',' ) { return c; } else { ERRORPRINTF("Unexpected character after last quote: %c, whole line is %s", *c, line); return NULL; } } else { ERRORPRINTF("Unexpected end of line: %s\n", line); return NULL; } } /** Reads write event from strace file. * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_write(char * line, list_t * list) { write_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; char * line2; op_item = new_write_item(); op_item->type = OP_WRITE; //first portion if ((retval = sscanf(line, " %d %s %*[^(](%d, ", &op_item->o.info.pid, start_time, &op_item->o.fd)) == EOF) { ERRORPRINTF("Error: unexpected end of line%s", "\n"); free(op_item); return -1; } if (retval != 3) { ERRORPRINTF("Error: It was not able to match all fields required:%d\n", retval); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } //second part line2 = strace_pos_comma(line); if (line2 == NULL || (retval = sscanf(line2, ", %"SCNi64") = %"SCNi64"%*[^<]<%[^>]", &op_item->o.size, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of line%s", "\n"); free(op_item); return -1; } if (retval != 3) { ERRORPRINTF("Error: It was not able to match all fields required while parsing line2:%d\n", retval); ERRORPRINTF("Failing line:%s", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads pwrite(2) syscall from strace file. * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_pwrite(char * line, list_t * list) { pwrite_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; char * line2; op_item = new_pwrite_item(); op_item->type = OP_PWRITE; //first portion if ((retval = sscanf(line, " %d %s %*[^(](%d, ", &op_item->o.info.pid, start_time, &op_item->o.fd)) == EOF) { ERRORPRINTF("Error: unexpected end of line%s", "\n"); free(op_item); return -1; } if (retval != 3) { ERRORPRINTF("Error: It was not able to match all fields required:%d\n", retval); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } //second part line2 = strace_pos_comma(line); if (line2 == NULL || (retval = sscanf(line2, ", %"SCNi64", %"SCNi64") = %"SCNi64"%*[^<]<%[^>]", &op_item->o.size, &op_item->o.offset, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of line%s", "\n"); free(op_item); return -1; } if (retval != 4) { ERRORPRINTF("Error: It was not able to match all fields required while parsing line2:%d\n", retval); ERRORPRINTF("Failing line:%s", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads pread(2) syscall from strace file. * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_pread(char * line, list_t * list) { pread_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; char * line2; op_item = new_pread_item(); op_item->type = OP_PREAD; //first portion if ((retval = sscanf(line, " %d %s %*[^(](%d, ", &op_item->o.info.pid, start_time, &op_item->o.fd)) == EOF) { ERRORPRINTF("Error: unexpected end of line%s", "\n"); free(op_item); return -1; } if (retval != 3) { ERRORPRINTF("Error: It was not able to match all fields required:%d\n", retval); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } //second part line2 = strace_pos_comma(line); if (line2 == NULL || (retval = sscanf(line2, ", %"SCNi64", %"SCNi64") = %"SCNi64"%*[^<]<%[^>]", &op_item->o.size, &op_item->o.offset, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of line%s", "\n"); free(op_item); return -1; } if (retval != 4) { ERRORPRINTF("Error: It was not able to match all fields required while parsing line2:%d\n", retval); ERRORPRINTF("Failing line:%s", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads read event from strace file. * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_read(char * line, list_t * list) { read_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; char * line2; op_item = new_read_item(); op_item->type = OP_READ; //first portion if ((retval = sscanf(line, " %d %s %*[^(](%d, ", &op_item->o.info.pid, start_time, &op_item->o.fd)) == EOF) { ERRORPRINTF("Error: unexpected end of line%s", "\n"); free(op_item); return -1; } if (retval != 3) { ERRORPRINTF("Error: It was not able to match all fields required:%d\n", retval); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } //second part line2 = strace_pos_comma(line); if (line2 == NULL || (retval = sscanf(line2, ", %"SCNi64") = %"SCNi64"%*[^<]<%[^>]", &op_item->o.size, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of line%s", "\n"); free(op_item); return -1; } if (retval != 3) { ERRORPRINTF("Error: It was not able to match all fields required while parsing line2:%d\n", retval); ERRORPRINTF("Failing line:%s", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads close event from strace file. * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_close(char * line, list_t * list) { close_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING] = "0"; op_item = new_close_item(); op_item->type = OP_CLOSE; if ((retval = sscanf(line, " %d %s %*[^(](%d) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, &op_item->o.fd, &op_item->o.retval, dur)) == EOF) { free(op_item); ERRORPRINTF("Error: unexpected end of file%s", "\n"); return -1; } // DEBUGPRINTF("pid:%d, start:%s, fd:%d, retval:%d, dur:%s\n", op_item->o.info.pid, start_time, op_item->o.fd, op_item->o.retval, dur); // if (retval != 5) { ERRORPRINTF("Error: Only %d parameters parsed\n", retval); ERRORPRINTF("Error: It was not able to match all fields required.%s", "\n"); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } if ( op_item->o.retval == -1 ) { // DEBUGPRINTF("Previous close failed...%s", "\n"); } if ( op_item->o.fd == -1 ) { DEBUGPRINTF("Closing -1 previously???...%s", "\n"); } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads mkdir event from strace file. * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_mkdir(char * line, list_t * list) { mkdir_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; op_item = new_mkdir_item(); op_item->type = OP_MKDIR; if ((retval = sscanf(line, "%d %s %*[^\"]\"%" QUOTE(MAX_STRING) "[^\"]\", %o) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, op_item->o.name, &op_item->o.mode, &op_item->o.retval, dur)) == EOF) { free(op_item); ERRORPRINTF("Error: unexpected end of file%s", "\n"); return -1; } if (retval != 6) { ERRORPRINTF("Error: Only %d parameters parsed\n", retval); ERRORPRINTF("Error: It was not able to match all fields required.%s", "\n"); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads rmdir event from strace file. * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_rmdir(char * line, list_t * list) { rmdir_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; op_item = new_rmdir_item(); op_item->type = OP_RMDIR; if ((retval = sscanf(line, "%d %s %*[^\"]\"%" QUOTE(MAX_STRING) "[^\"]\") = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, op_item->o.name, &op_item->o.retval, dur)) == EOF) { free(op_item); ERRORPRINTF("Error: unexpected end of file%s", "\n"); return -1; } if (retval != 5) { ERRORPRINTF("Error: Only %d parameters parsed\n", retval); ERRORPRINTF("Error: It was not able to match all fields required.%s", "\n"); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads unlink event from strace file. * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_unlink(char * line, list_t * list) { unlink_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; op_item = new_unlink_item(); op_item->type = OP_UNLINK; if ((retval = sscanf(line, "%d %s %*[^\"]\"%"QUOTE(MAX_STRING)"[^\"]\") = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, op_item->o.name, &op_item->o.retval, dur)) == EOF) { DEBUGPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 5) { ERRORPRINTF("Error: It was not able to match all fields required.%s", "\n"); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads pipe event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_pipe(char * line, list_t * list) { pipe_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; op_item = new_pipe_item(); op_item->type = OP_PIPE; op_item->o.mode = 0; if ((retval = sscanf(line, " %d %s %*[^[][%d, %d]) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, &op_item->o.fd1, &op_item->o.fd2, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 6) { //mode flag was not present there ERRORPRINTF("Error: It was not able to match all fields required.%s", "\n"); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads open event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_open(char * line, list_t * list) { open_item_t * op_item; char flags[MAX_STRING]; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING] = "0"; op_item = new_open_item(); op_item->type = OP_OPEN; if ((retval = sscanf(line, "%d %s %*[^\"]\"%" QUOTE(MAX_STRING) "[^\"]\", %[^,], %u) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, op_item->o.name, flags, &op_item->o.mode, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 7) { //mode was probably missing there if ((retval = sscanf(line, "%d %s %*[^\"]\"%" QUOTE(MAX_STRING) "[^\"]\", %[^)]) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, op_item->o.name, flags, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } // DEBUGPRINTF("pid:%d, start:%s, name:%s, flags:%s, retval:%d, dur:%s\n", op_item->o.info.pid, start_time, op_item->o.name, flags, op_item->o.retval, dur); if ( retval != 6) { ERRORPRINTF("Error: It was not able to match all fields required: %d\n", retval); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.mode = MODE_UNDEF; } op_item->o.flags = read_open_flags(flags); op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads creat event from strace file. It produces open event with correct mode. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_creat(char * line, list_t * list) { open_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING] = "0"; op_item = new_open_item(); op_item->type = OP_OPEN; if ((retval = sscanf(line, "%d %s %*[^\"]\"%" QUOTE(MAX_STRING) "[^\"]\", %u) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, op_item->o.name, &op_item->o.mode, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if ( retval != 6) { ERRORPRINTF("Error: It was not able to match all fields required: %d\n", retval); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.flags = O_CREAT|O_WRONLY|O_TRUNC; op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads clone event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_clone(char * line, list_t * list) { clone_item_t * op_item; char mode[MAX_STRING]; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; op_item = new_clone_item(); op_item->type = OP_CLONE; if ((retval = sscanf(line, "%d %s %*[^\(](%*[^,], flags=%[^,], %*[^)]) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, mode, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 5) { //mode flag was not present there ERRORPRINTF("Error: It was not able to match all fields required.%s", "\n"); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.mode = read_clone_flags(mode); DEBUGPRINTF("pid:%d, start:%s, flags:%s, retval:%d, dur:%s\n", op_item->o.info.pid, start_time, mode, op_item->o.retval, dur); op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads dup event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_dup(char * line, list_t * list) { dup_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; op_item = new_dup_item(); op_item->type = OP_DUP; op_item->o.flags = 0; if ((retval = sscanf(line, "%d %s %*[^(](%d) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, &op_item->o.old_fd, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 5) { //mode flag was not present there ERRORPRINTF("Error: It was not able to match all fields required.%s", "\n"); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.new_fd = op_item->o.retval; op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads dup2 event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_dup2(char * line, list_t * list) { dup_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; op_item = new_dup_item(); op_item->type = OP_DUP2; op_item->o.flags = 0; if ((retval = sscanf(line, "%d %s %*[^(](%d, %d) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, &op_item->o.old_fd, &op_item->o.new_fd, &op_item->o.retval, dur)) == EOF) { free(op_item); ERRORPRINTF("Error: unexpected end of file%s", "\n"); return -1; } if (retval != 6) { //mode flag was not present there ERRORPRINTF("Error: It was not able to match all fields required.%s", "\n"); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads dup3 event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_dup3(char * line, list_t * list) { dup_item_t * op_item; int retval; char flags[MAX_STRING]; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; op_item = new_dup_item(); op_item->type = OP_DUP3; if ((retval = sscanf(line, "%d %s %*[^(](%d, %d, %[^)]) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, &op_item->o.old_fd, &op_item->o.new_fd, flags, &op_item->o.retval, dur)) == EOF) { free(op_item); ERRORPRINTF("Error: unexpected end of file%s", "\n"); return -1; } if (retval != 7) { //mode flag was not present there ERRORPRINTF("Error: It was not able to match all fields required.%s", "\n"); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.flags = read_dup3_flags(flags); op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads llseek event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_llseek(char * line, list_t * list) { llseek_item_t * op_item; char flags[MAX_STRING]; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING]; op_item = new_llseek_item(); op_item->type = OP_LLSEEK; if ((retval = sscanf(line, " %d %s %*[^(](%d, %"SCNi64", \[%"SCNi64"], %[^)]) = %"SCNi64"%*[^<]<%[^>]", &op_item->o.info.pid, start_time, &op_item->o.fd, &op_item->o.offset, &op_item->o.f_offset, flags, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 8) { ERRORPRINTF("Error: It was not able to match all fields required.%s", "\n"); ERRORPRINTF("Failing line: %s", line); free(op_item); return -1; } op_item->o.flag = read_seek_flag(flags); op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads lseek event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_lseek(char * line, list_t * list) { lseek_item_t * op_item; char flags[MAX_STRING]; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING] = "0"; op_item = new_lseek_item(); op_item->type = OP_LSEEK; if ((retval = sscanf(line, "%d %s %*[^(](%d, %"SCNi64", %[^)]) = %"SCNi64"%*[^<]<%[^>]", &op_item->o.info.pid, start_time, &op_item->o.fd, &op_item->o.offset, flags, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } // DEBUGPRINTF("pid:%d, start:%s, fd:%d, offset: %d, flags:%s, retval:%u, dur:%s\n", op_item->o.info.pid, start_time, op_item->o.fd, op_item->o.offset, flags, op_item->o.retval, dur); if (retval != 7) { ERRORPRINTF("Error: It was not able to match all fields required :%d\n", retval); ERRORPRINTF("Failing line: %s\n", line); free(op_item); return -1; } op_item->o.flag = read_seek_flag(flags); op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads sendfile event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_sendfile(char * line, list_t * list) { sendfile_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING] = "0"; op_item = new_sendfile_item(); op_item->type = OP_SENDFILE; if ((retval = sscanf(line, "%d %s %*[^(](%d, %d, \[%"SCNi64"], %"SCNi64") = %"SCNi64"%*[^<]<%[^>]", &op_item->o.info.pid, start_time, &op_item->o.out_fd, &op_item->o.in_fd, &op_item->o.offset, &op_item->o.size, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 8) { if (retval == 4) { //maybe there is "NULL" string instead of [NUMBER] in fifth argument if ((retval = sscanf(line, "%d %s %*[^(](%d, %d, NULL, %"SCNi64") = %"SCNi64"%*[^<]<%[^>]", &op_item->o.info.pid, start_time, &op_item->o.out_fd, &op_item->o.in_fd, &op_item->o.size, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 7) { ERRORPRINTF("Error: It was not able to match all fields required :%d\n", retval); ERRORPRINTF("Failing line: %s\n", line); free(op_item); return -1; } else { op_item->o.offset = OFFSET_INVAL; } } else { ERRORPRINTF("Error: It was not able to match all fields required :%d\n", retval); ERRORPRINTF("Failing line: %s\n", line); free(op_item); return -1; } } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads access event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_access(char * line, list_t * list) { access_item_t * op_item; char mode[MAX_STRING]; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING] = "0"; op_item = new_access_item(); op_item->type = OP_ACCESS; if ((retval = sscanf(line, "%d %s %*[^\"]\"%" QUOTE(MAX_STRING) "[^\"]\", %[^)]) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, op_item->o.name, mode, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 6) { ERRORPRINTF("Error: It was not able to match all fields required: %d\n", retval); ERRORPRINTF("Failing line: %s\n", line); free(op_item); return -1; } op_item->o.mode = read_access_flags(mode); op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads stat event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int strace_read_stat(char * line, list_t * list) { stat_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING] = "0"; op_item = new_stat_item(); op_item->type = OP_STAT; if ((retval = sscanf(line, "%d %s %*[^\"]\"%" QUOTE(MAX_STRING) "[^\"]\", %*[^)]) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, op_item->o.name, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 5) { ERRORPRINTF("Error: It was not able to match all fields required: %d\n", retval); ERRORPRINTF("Failing line: %s\n", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads socket event from strace file. * * * @arg f file from which to read, must be opened * @arg list list to which to append new structure * @return 0 on success, non-zero otherwise */ int read_socket_strace(char * line, list_t * list) { socket_item_t * op_item; int retval; char start_time[MAX_TIME_STRING]; char dur[MAX_TIME_STRING] = "0"; op_item = new_socket_item(); op_item->type = OP_SOCKET; if ((retval = sscanf(line, "%d %s %*[^)]) = %d%*[^<]<%[^>]", &op_item->o.info.pid, start_time, &op_item->o.retval, dur)) == EOF) { ERRORPRINTF("Error: unexpected end of file%s", "\n"); free(op_item); return -1; } if (retval != 4) { ERRORPRINTF("Error: It was not able to match all fields required:%d\n",retval); ERRORPRINTF("Failing line: %s\n", line); free(op_item); return -1; } op_item->o.info.start = read_time(start_time); op_item->o.info.dur = read_duration(dur); list_append(list, &op_item->item); return 0; } /** Reads operation identifier from @a line. * * @arg line one line from strace output * @arg operation output buffer where operation will be written * @arg stats whether to count statistics for operations */ inline void strace_get_operation(char * line, char * operation, int stats) { char * c = line; int i; int retval; int32_t sec, usec; // skip first part containg PID and time of the syscall while (*c && (isspace(*c) || isdigit(*c) || *c == '.' || *c == '<' )) { c++; } i = 0; while ( *c && ((*c) != '(' && (*c) != ' ') ) { //read only operation - it can end with '(' in normal case or with ' ' in resumed calls operation[i] = *c; c++; i++; } operation[i] = 0; if (stats) { if ((retval = sscanf(line, "%*[^=]= %*[^<]<%d.%d>", &sec, &usec)) == EOF) { //probably unfinished call, ignore it return; } if ( retval != 2 ) { ERRORPRINTF("Error finding duration for statistics on line %s", line); return; } stats_add_op(line, operation, sec*1000000 + usec); } } /** Returns code of the operation in the given line. Also counts statistics for every operation (even no IO oriented), * if enabled. * * @arg line line from which to read operation code * @arg whether to record statistics or not * @return code of the operation on the line */ char strace_get_operation_code(char * line, int stats) { char operation[MAX_STRING]; strace_get_operation(line, operation, stats); if (! strcmp(operation, "write")) { return OP_WRITE; } else if (! strcmp(operation, "read")) { return OP_READ; } else if (! strcmp(operation, "pwrite")) { return OP_PWRITE; } else if (! strcmp(operation, "pwrite64")) { return OP_PWRITE; } else if (! strcmp(operation, "pread")) { return OP_PREAD; } else if (! strcmp(operation, "pread64")) { return OP_PREAD; } else if (! strcmp(operation, "close")) { return OP_CLOSE; } else if (! strcmp(operation, "open")) { return OP_OPEN; } else if (! strcmp(operation, "creat")) { return OP_CREAT; } else if (! strcmp(operation, "unlink")) { return OP_UNLINK; } else if (! strcmp(operation, "lseek")) { return OP_LSEEK; } else if (! strcmp(operation, "_llseek")) { return OP_LLSEEK; } else if (! strcmp(operation, "pipe2")) { return OP_UNKNOWN; ///< this is on purpose. Change in the future, when pipe2 is supported } else if (! strcmp(operation, "pipe")) { return OP_PIPE; } else if (! strcmp(operation, "dup3")) { return OP_DUP3; } else if (! strcmp(operation, "dup2")) { return OP_DUP2; } else if (! strcmp(operation, "dup")) { return OP_DUP; } else if (! strcmp(operation, "mkdir")) { return OP_MKDIR; } else if (! strcmp(operation, "clone")) { return OP_CLONE; } else if (! strcmp(operation, "access")) { return OP_ACCESS; } else if (! strcmp(operation, "stat64")) { return OP_STAT; } else if (! strcmp(operation, "stat")) { return OP_STAT; } else if (! strcmp(operation, "socket")) { return OP_SOCKET; } else if (! strcmp(operation, "sendfile")) { return OP_SENDFILE; } else if (! strcmp(operation, "fcntl")) { return OP_FCNTL; } return OP_UNKNOWN; } void strace_read_unfinished(char * line, hash_table_t * ht) { isyscall_t * isyscall; item_t * item; int pid; sscanf(line, "%d", &pid); if ( (item = hash_table_find(ht, &pid)) == NULL) { isyscall = malloc(sizeof(isyscall_t)); item_init(&isyscall->item); strncpy(isyscall->line, line, MAX_STRING); isyscall->pid = pid; hash_table_insert(ht, &pid, &isyscall->item); //DEBUGPRINTF("Unfinished call for %d inserted:%s", pid, line); } else { isyscall = hash_table_entry(item, isyscall_t, item); ERRORPRINTF("Already have unfinished syscall for pid: %d. %s", pid, isyscall->line); } } void strace_read_resumed(char * line, list_t * list, hash_table_t * ht) { isyscall_t * isyscall; item_t * item; char buff[MAX_STRING *2 + 1]; char *s; int pid; sscanf(line, "%d", &pid); if ( (item = hash_table_find(ht, &pid)) == NULL) { ERRORPRINTF("No syscall to resume for pid: %d. %s", pid, line); } else { isyscall = hash_table_entry(item, isyscall_t, item); s = strstr(isyscall->line, " "); if (s == NULL) { ERRORPRINTF("Previously recorded syscall was wrong: %d. %s", pid, isyscall->line); return; } *s = 0; strncpy(buff, isyscall->line, 2* MAX_STRING); s = strstr(line, "resumed> "); if (s == NULL) { ERRORPRINTF("Resumed syscall incorrectly formated: %d. %s", pid, isyscall->line); return; } s += strlen("resumed> "); strncat(buff, s, 2*MAX_STRING); //DEBUGPRINTF("Resulting line is :%s", buff); hash_table_remove(ht, &pid); strace_process_line(buff, list, ht, 0); //stats were already counted in first part } } inline int strace_process_line(char * line, list_t * list, hash_table_t * ht, int stats) { int retval = 0; char c; char *s; c = strace_get_operation_code(line, stats); if (strstr(line, "unfinished") && c != OP_UNKNOWN) { strace_read_unfinished(line, ht); return 0; } if ((s = strstr(line, "resumed")) != NULL) { if (s !=line) { s--; *s = '('; //lets hack the line, so it is recognized if ( strace_get_operation_code(line, 0) != OP_UNKNOWN) { //stats are disabled here, because it was already counted 5lines above strace_read_resumed(line, list, ht); } return 0; } } //just for now. int32_t pid; char start_time[20]; char dur[20]; int32_t old_fd; int32_t new_fd; int32_t tmp; switch(c) { case OP_WRITE: if ( (retval = strace_read_write(line, list)) != 0) { return retval; } break; case OP_READ: if ( (retval = strace_read_read(line, list)) != 0) { return retval; } break; case OP_PWRITE: if ( (retval = strace_read_pwrite(line, list)) != 0) { return retval; } break; case OP_PREAD: if ( (retval = strace_read_pread(line, list)) != 0) { return retval; } break; case OP_LSEEK: if ( (retval = strace_read_lseek(line, list)) != 0) { return retval; } break; case OP_LLSEEK: if ( (retval = strace_read_llseek(line, list)) != 0) { return retval; } break; case OP_OPEN: if ( (retval = strace_read_open(line, list)) != 0) { return retval; } break; case OP_CREAT: if ( (retval = strace_read_creat(line, list)) != 0) { return retval; } break; case OP_CLOSE: if ( (retval = strace_read_close(line, list)) != 0) { return retval; } break; case OP_UNLINK: if ( (retval = strace_read_unlink(line, list)) != 0) { return retval; } break; case OP_CLONE: if ( (retval = strace_read_clone(line, list)) != 0) { return retval; } break; case OP_DUP3: if ( (retval = strace_read_dup3(line, list)) != 0) { return retval; } break; case OP_DUP2: if ( (retval = strace_read_dup2(line, list)) != 0) { return retval; } break; case OP_DUP: if ( (retval = strace_read_dup(line, list)) != 0) { return retval; } break; case OP_PIPE: if ( (retval = strace_read_pipe(line, list)) != 0) { return retval; } break; case OP_MKDIR: if ( (retval = strace_read_mkdir(line, list)) != 0) { return retval; } break; case OP_RMDIR: if ( (retval = strace_read_rmdir(line, list)) != 0) { return retval; } break; case OP_ACCESS: if ( (retval = strace_read_access(line, list)) != 0) { return retval; } break; case OP_STAT: if ( (retval = strace_read_stat(line, list)) != 0) { return retval; } break; case OP_SOCKET: if ( (retval = read_socket_strace(line, list)) != 0) { return retval; } break; case OP_SENDFILE: if ( (retval = strace_read_sendfile(line, list)) != 0) { return retval; } break; case OP_FCNTL: //just for now. if ( strstr(line, "F_DUPFD")) { retval = sscanf(line, "%d %s %*[^(](%d, F_DUPFD, %d) = %d%*[^<]<%[^>]", &pid, start_time, &old_fd, &tmp, &new_fd, dur); if (retval != 6 ) { ERRORPRINTF("Can not parse line:, %s", line); return -1; } else { sprintf(line, "%d %s dup(%d) = %d <%s>", pid, start_time, old_fd, new_fd, dur); } if ( (retval = strace_read_dup(line, list)) != 0) { return retval; } } break; case OP_UNKNOWN: // just skip unknown operations break; default: return -1; break; } return retval; } /** Reads file syscalls actions from @a filename which is formatted output of strace program. * Detailed format of the input file is described in README under "strace file data structure". * All informations are appended to the @a list. * * @arg filename filename from which to read input * @arg list initialized pointer to the the. * @return 0 on success, error code otherwise */ int strace_get_items(char * filename, list_t * list, int stats) { FILE * f; char line[MAX_LINE]; hash_table_t ht; int retval; int linenum = 0; if ( (f = fopen(filename, "r")) == NULL) { DEBUGPRINTF("Error opening file %s: %s\n", filename, strerror(errno)); return errno; } hash_table_init(&ht, HASH_TABLE_SIZE, &ht_ops_isyscall); if (stats) { stats_init(); } while(fgets(line, MAX_LINE, f) != NULL) { linenum++; retval = strace_process_line(line, list, &ht, stats); if (retval != 0) { ERRORPRINTF("Error parsing file %s: on line %d, position %ld\n", filename, linenum, ftell(f)); } } if (stats) { stats_print(); } hash_table_destroy(&ht); fclose(f); return 0; } ioapps-1.4-r2/CHANGELOG0000644000175000017500000000333312134167044013776 0ustar keruomkeruomv1.4-r2 * Fix: license and copyright information in all source files -- Jiri Horky Fri, 19 Apr 2013 09:10:05 +0200 v1.4-r1 * Fix: tarball included .svn files * Fix: ioprofiler.1 manpage is now included in install * Fix: Makefiles cleanup * No new features -- Jiri Horky Fri, 09 Apr 2013 20:20:05 +0200 v1.4 * New feature: ioprofiler can open traces from a file specified on the command line as an argument * New feature: Help menu for ioprofiler to aid people understand what it is useful for and to point to wiki * New feature: ioprofiler-trace wrapper around strace to help getting traces * Bug fix: Histograms now show 99% of the data - scale is automatically adjusted * Bug fix: "Show stats" button in ioprofiler works for histograms as well * Some minor bug fixes -- Jiri Horky Fri, 05 Apr 2013 14:20:05 +0200 v1.3 * New feature: Ioprofiler can now read incomplete traces * New feature: Processes' info is now kept in hash tables to improve performance * Bug fix: various bugs fixed -- Jiri Horky Wed, 21 Sep 2011 08:59:05 +0200 v1.2 * New feature: support for sendfile syscall * New feature: support for incomplete traces (obtained by strace -p) for ioreplay -- Jiri Horky Sun, 10 Apr 2011 11:36:22 +0100 v1.1 * Bug fixed: ioprofiler can now re-open files without restarting the application * Bug fixed: some memory leaks and minor bug fixed * New feature: support for pread() and pwrite() system calls added -- Jiri Horky Thu, 02 Jan 2011 12:56:30 +0100 v1.0 * Initial release -- Jiri Horky Thu, 02 Nov 2010 20:50:01 +0100 ioapps-1.4-r2/stats.h0000644000175000017500000000272112133726077014101 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _STATS_H_ #define _STATS_H_ /** @file stats.h * * Counts statistics for every syscall. * * It uses hash_table, where the key is a string and data are stats_item_t strutcs, which for every syscall contain * count of their occurence. */ #include #include "in_common.h" #include "adt/hash_table.h" #define MAX_OPERATION 30 typedef struct statistic_item { char operation[MAX_OPERATION]; uint64_t count; double sumdur; ///< total duration of this type of operation in ms item_t item; } statistic_item_t; void stats_add_op(const char * line, const char * operation, int32_t duration); void stats_print(); void stats_init(); #endif ioapps-1.4-r2/print.h0000644000175000017500000000167012133726077014101 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _PRINT_H_ #define _PRINT_H_ #include #include "common.h" int print_items(list_t * list); #endif ioapps-1.4-r2/stats.c0000644000175000017500000000563312133726077014101 0ustar keruomkeruom/* IOapps, IO profiler and IO traces replayer Copyright (C) 2010 Jiri Horky 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. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "stats.h" hash_table_t g_stats_ht; static int ht_compare_stat(key_t *key, item_t *item) { statistic_item_t * statistic_item; statistic_item = hash_table_entry(item, statistic_item_t, item); return ! strncmp(statistic_item->operation, (char *) key, MAX_STRING); } static inline void ht_remove_callback_stat(item_t * item) { statistic_item_t * statistic_item = hash_table_entry(item, statistic_item_t, item); free(statistic_item); } /** hash table operations. */ static hash_table_operations_t ht_ops_stat = { .hash = ht_hash_str, .compare = ht_compare_stat, .remove_callback = ht_remove_callback_stat /* = NULL if not used */ }; /** Adds operation to statistics. * * @arg line line on which operation occured. Currently unused. * @arg operation string determining operation */ void stats_add_op(const char * line, const char * operation, int32_t duration) { item_t * item; statistic_item_t * statistic_item; if ( (item = hash_table_find(&g_stats_ht, (key_t *) operation)) == NULL) { statistic_item = malloc(sizeof(statistic_item_t)); item_init(&statistic_item->item); strncpy(statistic_item->operation, operation, MAX_OPERATION); statistic_item->count = 1; statistic_item->sumdur = duration/1000.0; hash_table_insert(&g_stats_ht,(key_t *) statistic_item->operation, &statistic_item->item); } else { statistic_item = hash_table_entry(item, statistic_item_t, item); statistic_item->count++; statistic_item->sumdur += duration/1000.0; } } /** Prints one stats item * @arg item item to print */ void stats_print_item(item_t * item) { statistic_item_t * statistic_item = hash_table_entry(item, statistic_item_t, item); printf("%s : %"PRIu64" (%0.2lfms)\n", statistic_item->operation, statistic_item->count, statistic_item->sumdur); } /** Prints statistics for every operation. */ void stats_print() { hash_table_apply(&g_stats_ht, stats_print_item); } /** Initialize statistics structure. This function must be called before any other stats_* call. */ void stats_init() { hash_table_init(&g_stats_ht, HASH_TABLE_SIZE, &ht_ops_stat); }