diff options
Diffstat (limited to 'glusterfs-guts/src/guts-trace.c')
-rw-r--r-- | glusterfs-guts/src/guts-trace.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/glusterfs-guts/src/guts-trace.c b/glusterfs-guts/src/guts-trace.c new file mode 100644 index 000000000..51d8a68d6 --- /dev/null +++ b/glusterfs-guts/src/guts-trace.c @@ -0,0 +1,650 @@ +/* + Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com> + This file is part of GlusterFS. + + GlusterFS 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 3 of the License, + or (at your option) any later version. + + GlusterFS 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, see + <http://www.gnu.org/licenses/>. +*/ + +#include "glusterfs-guts.h" +#include <signal.h> + +#include "guts-parse.h" +#include "guts-tables.h" +#include "guts-trace.h" + +static xlator_t * +fuse_graph (xlator_t *graph) +{ + xlator_t *top = NULL; + xlator_list_t *xlchild; + + top = CALLOC (1, sizeof (*top)); + ERR_ABORT (top); + xlchild = CALLOC (1, sizeof(*xlchild)); + ERR_ABORT (xlchild); + xlchild->xlator = graph; + top->children = xlchild; + top->ctx = graph->ctx; + top->next = graph; + graph->parent = top; + + return top; +} + +int32_t +fuse_thread (pthread_t *thread, void *data); + +int32_t +guts_trace (guts_ctx_t *guts_ctx) +{ + transport_t *mp = NULL; + glusterfs_ctx_t ctx = { + .poll_type = SYS_POLL_TYPE_EPOLL, + }; + xlator_t *graph = NULL; + call_pool_t *pool = NULL; + int32_t ret = -1; + pthread_t thread; + /* Ignore SIGPIPE */ + signal (SIGPIPE, SIG_IGN); + +#if HAVE_BACKTRACE + /* Handle SIGABORT and SIGSEGV */ + signal (SIGSEGV, gf_print_trace); + signal (SIGABRT, gf_print_trace); +#endif /* HAVE_BACKTRACE */ + + ret = guts_tio_init (guts_ctx->file); + + if (ret < 0) { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "running in trace mode: failed to open tio file %s", guts_ctx->file); + return -1; + } + + pool = ctx.pool = CALLOC (1, sizeof (call_pool_t)); + ERR_ABORT (ctx.pool); + LOCK_INIT (&pool->lock); + INIT_LIST_HEAD (&pool->all_frames); + + /* glusterfs_mount has to be ideally placed after all the initialisation stuff */ + if (!(mp = glusterfs_mount (&ctx, guts_ctx->mountpoint))) { + gf_log ("glusterfs-guts", GF_LOG_ERROR, "Unable to mount glusterfs"); + return -1; + } + + gf_timer_registry_init (&ctx); + graph = guts_ctx->graph; + + if (!graph) { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "Unable to get xlator graph for mount_point %s", guts_ctx->mountpoint); + transport_disconnect (mp); + return -1; + } + + ctx.graph = graph; + + mp->xl = fuse_graph (graph); + mp->xl->ctx = &ctx; + + fuse_thread (&thread, mp); + + while (!poll_iteration (&ctx)); + + return 0; +} + + + +static void +guts_name (struct fuse_in_header *in, + const void *inargs) +{ + char *name = (char *) inargs; + + guts_req_dump (in, name, strlen (name)); +} + +static void +guts_noarg (struct fuse_in_header *in, + const void *inargs) +{ + guts_req_dump (in, NULL, 0); +} + + +static void +guts_setattr (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_setattr_in *arg = (struct fuse_setattr_in *)inargs; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_access (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_access_in *arg = (struct fuse_access_in *)inargs; + guts_req_dump (in, arg, sizeof (*arg)); +} + + +static void +guts_mknod (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inargs; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_mkdir (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inargs; + guts_req_dump (in, arg, sizeof (*arg)); +} + + +static void +guts_symlink (struct fuse_in_header *in, + const void *inargs) +{ + char *name = (char *) inargs; + char *linkname = ((char *) inargs) + strlen ((char *) inargs) + 1; + struct guts_symlink_in symlink_in; + + strcpy (symlink_in.name, name); + strcpy (symlink_in.linkname, linkname); + guts_req_dump (in, &symlink_in, sizeof (symlink_in)); +} + +static void +guts_rename (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_rename_in *arg = (struct fuse_rename_in *) inargs; + char *oldname = PARAM(arg); + char *newname = oldname + strlen (oldname) + 1; + struct guts_rename_in rename_in; + + memset (&rename_in, 0, sizeof (rename_in)); + memcpy (&rename_in, arg, sizeof (*arg)); + strcpy (rename_in.oldname, oldname); + strcpy (rename_in.newname, newname); + + guts_req_dump (in, &rename_in, sizeof (rename_in)); + +} + +static void +guts_link (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_link_in *arg = (struct fuse_link_in *) inargs; + + guts_req_dump (in, arg, sizeof (*arg)); +} + + +static void +guts_open (struct fuse_in_header *in, + const void *inargs) +{ + struct fuse_open_in *arg = (struct fuse_open_in *) inargs; + + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_create (struct fuse_in_header *in, + const void *inargs) +{ + struct guts_create_in create_in; + struct fuse_open_in *arg = (struct fuse_open_in *) inargs; + char *name = PARAM (arg); + + memset (&create_in, 0, sizeof (create_in)); + memcpy (&create_in.open_in, arg, sizeof (*arg)); + memcpy (&create_in.name, name, strlen (name)); + + guts_req_dump (in, &create_in, sizeof (create_in)); +} + + +static void +guts_read(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_read_in *arg = (struct fuse_read_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_write(struct fuse_in_header *in, + const void *inarg) +{ + /* TODO: where the hell is the data to be written??? */ + struct fuse_write_in *arg = (struct fuse_write_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_flush(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_release(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_release_in *arg = (struct fuse_release_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_fsync(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + + +static void +guts_readdir(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_read_in *arg = (struct fuse_read_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_releasedir(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_release_in *arg = (struct fuse_release_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +static void +guts_fsyncdir(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + + +static void +guts_setxattr(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; + char *name = PARAM(arg); + char *value = name + strlen(name) + 1; + struct guts_xattr_in setxattr_in; + + memset (&setxattr_in, 0, sizeof (setxattr_in)); + memcpy (&setxattr_in, arg, sizeof (*arg)); + strcpy (setxattr_in.name, name); + strcpy (setxattr_in.value, value); + + guts_req_dump (in, &setxattr_in, sizeof (setxattr_in)); + +} + +static void +guts_getxattr(struct fuse_in_header *in, + const void *inarg) +{ + struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; + guts_req_dump (in, arg, sizeof (*arg)); +} + +guts_log_t guts_log[] = { + [FUSE_LOOKUP] = { guts_name, "lookup" }, + [FUSE_GETATTR] = { guts_noarg, "getattr" }, + [FUSE_SETATTR] = { guts_setattr, "setattr" }, + [FUSE_ACCESS] = { guts_access, "access" }, + [FUSE_READLINK] = { guts_noarg, "readlink" }, + [FUSE_MKNOD] = { guts_mknod, "mknod" }, + [FUSE_MKDIR] = { guts_mkdir, "mkdir" }, + [FUSE_UNLINK] = { guts_name, "unlink" }, + [FUSE_RMDIR] = { guts_name, "rmdir" }, + [FUSE_SYMLINK] = { guts_symlink, "symlink" }, + [FUSE_RENAME] = { guts_rename, "rename" }, + [FUSE_LINK] = { guts_link, "link" }, + [FUSE_CREATE] = { guts_create, "create" }, + [FUSE_OPEN] = { guts_open, "open" }, + [FUSE_READ] = { guts_read, "read" }, + [FUSE_WRITE] = { guts_write, "write" }, + [FUSE_FLUSH] = { guts_flush, "flush" }, + [FUSE_RELEASE] = { guts_release, "release" }, + [FUSE_FSYNC] = { guts_fsync, "fsync" }, + [FUSE_OPENDIR] = { guts_open, "opendir" }, + [FUSE_READDIR] = { guts_readdir, "readdir" }, + [FUSE_RELEASEDIR] = { guts_releasedir, "releasedir" }, + [FUSE_FSYNCDIR] = { guts_fsyncdir, "fsyncdir" }, + [FUSE_STATFS] = { guts_noarg, "statfs" }, + [FUSE_SETXATTR] = { guts_setxattr, "setxattr" }, + [FUSE_GETXATTR] = { guts_getxattr, "getxattr" }, + [FUSE_LISTXATTR] = { guts_getxattr, "listxattr" }, + [FUSE_REMOVEXATTR] = { guts_name, "removexattr" }, +}; + +/* used for actual tracing task */ + +int32_t +guts_log_req (void *buf, + int32_t len) +{ + struct fuse_in_header *in = buf; + const void *inargs = NULL; + int32_t header_len = sizeof (struct fuse_in_header); + + if (header_len < len ) { + inargs = buf + header_len; + gf_log ("guts-gimmik", GF_LOG_ERROR, + "unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %zu\n", + (unsigned long long) in->unique, "<null>", + /*opname((enum fuse_opcode) in->opcode),*/ in->opcode, + (unsigned long) in->nodeid, len); + if (guts_log[in->opcode].func) + guts_log[in->opcode].func (in, inargs); + + } else { + gf_log ("guts", GF_LOG_ERROR, + "header is longer than the buffer passed"); + } + + return 0; +} + + +int +guts_reply_err (fuse_req_t req, int err) +{ + if (IS_TRACE(req)) { + /* we are tracing calls, just dump the reply to file and continue with fuse_reply_err() */ + guts_reply_dump (req, &err, sizeof (err)); + return fuse_reply_err (req, err); + } else { + /* we are replaying. ;) */ + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + int32_t opcode = guts_get_opcode (ctx, req->unique); + + /* see if we are called by close/closedir, if yes remove do a guts_fd_delete () */ + if (opcode == FUSE_RELEASEDIR || opcode == FUSE_RELEASE) { + guts_req_t *request = guts_lookup_request (ctx, req->unique); + struct fuse_release_in *arg = request->arg; + + guts_delete_fd (ctx, arg->fh); + } else if (err == -1) { + /* error while replaying?? just quit as of now + * TODO: this is not the right way */ + printf (":O - glusterfs-guts: replay failed\n"); + exit (0); + } + + return 0; + } +} + +void +guts_reply_none (fuse_req_t req) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, NULL, 0); + fuse_reply_none (req); + } else { + return; + } +} + +int +guts_reply_entry (fuse_req_t req, + const struct fuse_entry_param *e) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, e, sizeof (*e)); + return fuse_reply_entry (req, e); + } else { + /* TODO: is dict_set() the best solution for this case?? */ + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + struct fuse_entry_param *old_entry = (struct fuse_entry_param *)reply->arg; + guts_inode_update (ctx, old_entry->ino, e->ino); + return 0; + } +} + +int +guts_reply_create (fuse_req_t req, + const struct fuse_entry_param *e, + const struct fuse_file_info *f) +{ + if (IS_TRACE(req)) { + struct guts_create_out create_out; + + memset (&create_out, 0, sizeof (create_out)); + memcpy (&create_out.e, e, sizeof (*e)); + memcpy (&create_out.f, f, sizeof (*f)); + + guts_reply_dump (req, &create_out, sizeof (create_out)); + return fuse_reply_create (req, e, f); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + struct guts_create_out *old_createout = (struct guts_create_out *) reply->arg; + struct fuse_file_info *old_f = &old_createout->f; + + /* add a new fd and map it to the file handle, as stored in tio file */ + guts_fd_add (ctx, old_f->fh, (fd_t *)(long)f->fh); + + return 0; + } +} + + +int +guts_reply_attr (fuse_req_t req, + const struct stat *attr, + double attr_timeout) +{ + if (IS_TRACE(req)) { + struct guts_attr_out attr_out; + + memcpy (&attr_out.attr, attr, sizeof (*attr)); + attr_out.attr_timeout = attr_timeout; + + guts_reply_dump (req, &attr_out, sizeof (attr_out)); + return fuse_reply_attr (req, attr, attr_timeout); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + struct guts_attr_out *old_attrout = (struct guts_attr_out *) reply->arg; + + if (!guts_attr_cmp (attr, &old_attrout->attr)) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "attr failed."); + return -1; + } + } +} + +int +guts_reply_readlink (fuse_req_t req, + const char *linkname) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, linkname, strlen (linkname)); + return fuse_reply_readlink (req, linkname); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + char *old_linkname = (char *) reply->arg; + if (!strcmp (linkname, old_linkname)) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "readlink failed. linkname in tio file: %s \n linkname recieved on replay: %s", + old_linkname, linkname); + return -1; + } + } +} + +int +guts_reply_open (fuse_req_t req, + const struct fuse_file_info *f) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, f, sizeof (*f)); + return fuse_reply_open (req, f); + } else { + /* the fd we recieve here is the valid fd for our current session, map the indicative number we have + * in mapping */ + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + + if (reply) { + struct fuse_file_info *old_f = reply->arg; + + /* add a new fd and map it to the file handle, as stored in tio file */ + guts_fd_add (ctx, old_f->fh, (fd_t *)(long)f->fh); + } + + return 0; + } +} + +int +guts_reply_write (fuse_req_t req, + size_t count) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, &count, sizeof (count)); + return fuse_reply_write (req, count); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + size_t *old_count = reply->arg; + if (count == *old_count) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "writev failed. old writev count: %d \n writev count on replay: %d", + old_count, count); + return -1; + } + } +} + +int +guts_reply_buf (fuse_req_t req, + const char *buf, + size_t size) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, buf, size); + return fuse_reply_buf (req, buf, size); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + char *old_buf = reply->arg; + size_t old_size = reply->arg_len; + if ((size == old_size) && (!memcmp (buf, old_buf, size))) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "readv failed. old readv size: %d \n readv size on replay: %d", + old_size, size); + return -1; + } + } +} + +int +guts_reply_statfs (fuse_req_t req, + const struct statvfs *stbuf) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, stbuf, sizeof (*stbuf)); + return fuse_reply_statfs (req, stbuf); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + struct statvfs *old_stbuf = reply->arg; + + if (!guts_statvfs_cmp (old_stbuf, stbuf)) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "statfs failed."); + return -1; + } + } +} + +int +guts_reply_xattr (fuse_req_t req, + size_t count) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, &count, sizeof (count)); + return fuse_reply_xattr (req, count); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + size_t *old_count = reply->arg; + if (count == *old_count) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "xattr failed. old xattr count: %d \n xattr count on replay: %d", + old_count, count); + return -1; + } + } +} + +int +guts_reply_lock (fuse_req_t req, + struct flock *lock) +{ + if (IS_TRACE(req)) { + guts_reply_dump (req, lock , sizeof (*lock)); + return fuse_reply_lock (req, lock); + } else { + guts_replay_ctx_t *ctx = (guts_replay_ctx_t *) req->u.ni.data; + guts_reply_t *reply = guts_lookup_reply (ctx, req->unique); + struct flock *old_lock = (struct flock *)reply->arg; + if (!guts_flock_cmp (lock, old_lock)) + return 0; + else { + gf_log ("glusterfs-guts", GF_LOG_ERROR, + "lock failed."); + return -1; + } + } +} |