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 00000000000..51d8a68d602 --- /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; +    } +  } +}  | 
