summaryrefslogtreecommitdiffstats
path: root/glusterfs-guts/src/guts-trace.c
diff options
context:
space:
mode:
Diffstat (limited to 'glusterfs-guts/src/guts-trace.c')
-rw-r--r--glusterfs-guts/src/guts-trace.c650
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;
+ }
+ }
+}