This is the mail archive of the cluster-cvs@sourceware.org mailing list for the cluster.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

STABLE2 - dlm: Add dlmtop


Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=65d8849b826445ac01b465cbf6b30ab747256adc
Commit:        65d8849b826445ac01b465cbf6b30ab747256adc
Parent:        ddebb0c3dc7d0b87c402ba17731ad41abdd43f2d
Author:        Christine Caulfield <ccaulfie@redhat.com>
AuthorDate:    Mon Oct 20 16:15:52 2008 +0100
Committer:     Christine Caulfield <ccaulfie@redhat.com>
CommitterDate: Mon Oct 20 16:15:52 2008 +0100

dlm: Add dlmtop

Add dlmtop program (this needs some work still, but is quite functional).

Also, some small fixes for dlm_tcpdump on STABLE2/RHEL5

Signed-off-by: Christine Caulfield <ccaulfie@redhat.com>
---
 dlm/tests/tcpdump/Makefile      |   10 +-
 dlm/tests/tcpdump/dlm_tcpdump.c |    4 +-
 dlm/tests/tcpdump/dlmtop.c      |  613 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 620 insertions(+), 7 deletions(-)

diff --git a/dlm/tests/tcpdump/Makefile b/dlm/tests/tcpdump/Makefile
index f572e3b..3c63491 100644
--- a/dlm/tests/tcpdump/Makefile
+++ b/dlm/tests/tcpdump/Makefile
@@ -1,8 +1,8 @@
-TARGET= dlm_tcpdump
+TARGETS= dlm_tcpdump dlmtop
 
-SBINDIRT=$(TARGET)
+SBINDIRT=$(TARGETS)
 
-all: ${TARGET}
+all: ${TARGETS}
 
 include ../../../make/defines.mk
 include $(OBJDIR)/make/cobj.mk
@@ -10,9 +10,9 @@ include $(OBJDIR)/make/clean.mk
 include $(OBJDIR)/make/install.mk
 include $(OBJDIR)/make/uninstall.mk
 
-OBJS=	dlm_tcpdump.o
+OBJS= dlmtop.o
 
-CFLAGS += -I${incdir}
+CFLAGS += -I${incdir} -DSTABLE2
 
 LDFLAGS += -L${dlmlibdir} -lpcap
 
diff --git a/dlm/tests/tcpdump/dlm_tcpdump.c b/dlm/tests/tcpdump/dlm_tcpdump.c
index 655803d..6741888 100644
--- a/dlm/tests/tcpdump/dlm_tcpdump.c
+++ b/dlm/tests/tcpdump/dlm_tcpdump.c
@@ -77,9 +77,9 @@ struct dlm_message {
 	uint32_t		m_lockspace;
 	uint32_t		m_nodeid;	/* nodeid of sender */
 	uint16_t		m_length;
-	uint8_t			m_cmd;		/* DLM_MSG, DLM_RCOM */
+	uint8_t			m_msg;		/* DLM_MSG, DLM_RCOM */
 	uint8_t			m_pad;
-	uint32_t		m_type;		/* DLM_MSG_ */
+	uint32_t		m_msg;		/* DLM_MSG_ */
 	uint32_t		m_rnodeid;
 	uint32_t		m_pid;
 	uint32_t		m_lkid;		/* lkid on sender */
diff --git a/dlm/tests/tcpdump/dlmtop.c b/dlm/tests/tcpdump/dlmtop.c
new file mode 100644
index 0000000..38cf4e5
--- /dev/null
+++ b/dlm/tests/tcpdump/dlmtop.c
@@ -0,0 +1,613 @@
+/**********************************************************************
+*
+* DLMTOP Christine Caulfield <ccaulfie@redhat.com>
+*
+* (c) 2008 Red Hat Inc
+*
+* $ gcc dlmtop.c -o dlmtop -lpcap -DRHEL4
+*
+**********************************************************************/
+
+/*
+ * TODO:
+ *  - Print lockspace name rather than number (if possible)
+ *  - Print node name rather than address
+ *  - Fix bug where PIDs appear in both lists!
+ */
+
+#include <pcap.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <ifaddrs.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/if_ether.h>
+#include <time.h>
+
+static char *dlm_filter = "port 21064";
+
+#define VERSION "v0.1"
+#define MAX_PROCESSES 512
+#define NAME_LENGTH 1024
+
+#ifdef RHEL4
+#define DLM_LVB_LEN            (32)
+
+struct dlm_message {
+	uint32_t		m_version;
+	uint8_t			m_cmd;		/* What we are */
+	uint8_t			m_flags;	/* maybe just a pad */
+	uint16_t		m_length;	/* Length of struct (so we can
+						   send many in 1 message) */
+	uint32_t		m_lkid;     	/* Lock ID tag: ie the local
+						   (requesting) lock ID */
+	uint32_t		m_lockspace;	/* Lockspace ID */
+	uint32_t		m_remlkid;	/* Remote lock ID */
+	uint32_t		m_remparid;	/* Parent's remote lock ID */
+	uint32_t		m_lflags;	/* Flags from lock/convert req*/
+	uint64_t		m_range_start;  /* Yes, these are in the right
+						   place... */
+	uint64_t		m_range_end;
+	uint32_t		m_status;	/* Status to return if this is
+						   an AST request */
+        uint32_t                m_pid;          /* Owner PID of lock */
+	uint32_t		m_lvbseq;
+	uint8_t			m_rqmode;	/* Requested lock mode */
+	uint8_t			m_asts;         /* Whether the LKB has ASTs */
+	char			m_lvb[DLM_LVB_LEN];
+	char			m_name[1];	/* As long as needs be. Only
+						   used for directory lookups.
+						   The length of this can be
+						   worked out from the packet
+						   length */
+} __attribute__((packed));
+
+#define DLM_MSG_LOCK     2
+#define DLM_MSG_UNLOCK   3
+#define DLM_MSG_CONVERT  4
+
+#define DLM_VERSION      0x00010001
+
+#endif
+
+#if defined(RHEL5) || defined(STABLE2) || defined(STABLE3)
+
+struct dlm_message {
+	uint32_t		m_version;
+	uint32_t		m_lockspace;
+	uint32_t		m_nodeid;	/* nodeid of sender */
+	uint16_t		m_length;
+	uint8_t			m_msgtype;		/* DLM_MSG, DLM_RCOM */
+	uint8_t			m_pad;
+	uint32_t		m_cmd;		/* DLM_MSG_ */
+	uint32_t		m_rnodeid;
+	uint32_t		m_pid;
+	uint32_t		m_lkid;		/* lkid on sender */
+	uint32_t		m_remid;	/* lkid on receiver */
+	uint32_t		m_parent_lkid;
+	uint32_t		m_parent_remid;
+	uint32_t		m_exflags;
+	uint32_t		m_sbflags;
+	uint32_t		m_flags;
+	uint32_t		m_lvbseq;
+	uint32_t		m_hash;
+	int			m_status;
+	int			m_grmode;
+	int			m_rqmode;
+	int			m_bastmode;
+	int			m_asts;
+	int			m_result;	/* 0 or -EXXX */
+	char			m_name[0];	/* name or lvb */
+};
+
+#define DLM_MSG_LOCK     1
+#define DLM_MSG_UNLOCK   3
+#define DLM_MSG_CONVERT  2
+
+#define DLM_VERSION      0x00030000
+
+#endif
+
+#ifndef DLM_MSG_LOCK
+#error You must build for RHEL4, RHEL5, STABLE2 or STABLE2
+#endif
+
+/* Struct for a saved process */
+struct locking_process
+{
+	char name[NAME_LENGTH];
+	pid_t pid;
+	uint32_t lockspace;
+	in_addr_t nodeaddr;
+	unsigned int locks;
+	unsigned int convs;
+	unsigned int unlocks;
+	unsigned int others;
+	unsigned int resources;
+	unsigned int last_locks;
+	unsigned int last_convs;
+	unsigned int last_unlocks;
+};
+
+static int nondlm = 0;
+static int packets = 0;
+static int l_locks = 0;
+static int r_locks = 0;
+static int errors = 0;
+static struct timeval starttime;
+typedef enum {SORT_LOCKS, SORT_RESOURCES} sort_type_t;
+static sort_type_t sort_type = SORT_LOCKS;
+typedef enum {DISPLAY_ACCUM, DISPLAY_DELTA} display_type_t;
+static display_type_t display_type = DISPLAY_DELTA;
+
+static int interval = 1;
+static int debug = 0;
+
+static int term_width;
+static int term_height;
+
+static struct locking_process local_procs[MAX_PROCESSES];
+static struct locking_process remote_procs[MAX_PROCESSES];
+static int num_local_procs;
+static int num_remote_procs;
+
+/* This always suceeds */
+static struct locking_process *find_proc(struct locking_process *array, pid_t pid)
+{
+	int i;
+	int *array_size;
+	struct locking_process *proc;
+
+	if (array == &local_procs[0])
+		array_size = &num_local_procs;
+	else
+		array_size = &num_remote_procs;
+
+	for (i=0; i<*array_size; i++) {
+		if (array[i].pid == pid)
+			return &array[i];
+	}
+
+	/* If the array is full, then discard the last element in the hope that it's not
+	   much used anyway. */
+	proc = &array[*array_size];
+	if (*array_size < MAX_PROCESSES)
+		*array_size = *array_size + 1;
+
+	/* Clear out new entry in case it is being reused */
+	memset(proc, 0, sizeof(*proc));
+
+	return proc;
+}
+
+static int proc_compare(const void *a, const void *b)
+{
+	const struct locking_process *pa = a;
+	const struct locking_process *pb = b;
+
+	if (display_type == DISPLAY_DELTA) {
+		if (sort_type == SORT_LOCKS)
+			return (pb->locks - pb->last_locks) - (pa->locks - pa->last_locks);
+		else
+			return pb->resources - pa->resources;
+	}
+	else {
+		if (sort_type == SORT_LOCKS)
+			return pb->locks - pa->locks;
+		else
+			return pb->resources - pa->resources;
+	}
+}
+
+static void update_deltas(struct locking_process *array, int size)
+{
+	int i;
+
+	for (i=0; i<size; i++) {
+		array[i].last_locks = array[i].locks;
+		array[i].last_unlocks = array[i].unlocks;
+		array[i].last_convs = array[i].convs;
+	}
+}
+
+static void sort_proc(struct locking_process *array)
+{
+	int array_size;
+
+	if (array == &local_procs[0])
+		array_size = num_local_procs;
+	else
+		array_size = num_remote_procs;
+
+	qsort(array, array_size, sizeof(struct locking_process), proc_compare);
+}
+
+static char *get_command(pid_t pid)
+{
+	char name[1024];
+	static char cmd[1024];
+	int i;
+	FILE *f;
+
+	cmd[0] = '\0';
+	sprintf(name, "/proc/%d/cmdline", pid);
+	f = fopen(name, "r");
+	if (f) {
+		fgets(cmd, sizeof(cmd), f);
+
+		/* Cope with empty names */
+		if (!strlen(cmd))
+			return cmd;
+
+		/* Return the full command line */
+		for (i=0; i<sizeof(cmd); i++)
+			if ((cmd[i] == 0) &&
+			    (cmd[i+1] != 0))
+				cmd[i] = ' ';
+
+		fclose(f);
+	}
+	return cmd;
+}
+
+/* We assume that the lcoal host only has 1 Ipv4 address */
+static int is_local_ip(unsigned int test_ip)
+{
+	struct ifaddrs *ifa, *ifa_list;
+	struct sockaddr *sa;
+	struct sockaddr_in *sin;
+	static unsigned int local_ip = 0;
+
+	if (local_ip) {
+		if (local_ip == test_ip)
+			return 1;
+		else
+			return 0;
+	}
+	if (getifaddrs(&ifa_list))
+		return 0;
+
+	for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
+		sa = ifa->ifa_addr;
+		if (!sa || sa->sa_family != AF_INET)
+			continue;
+
+		sin = (struct sockaddr_in *)sa;
+
+		if (test_ip == sin->sin_addr.s_addr) {
+			local_ip = test_ip;
+			return 1;
+		}
+	}
+
+	freeifaddrs(ifa_list);
+	return 0;
+}
+
+/* Maybe we should use ncurses */
+static void display_local_procs()
+{
+	int num_procs = term_height/2 - 5;
+	int i;
+	char name[NAME_LENGTH];
+
+	printf("  pid   lockspace    locks   unlcks    convs      res   cmd\n");
+
+	for (i=0; i<num_procs; i++) {
+		if (i < num_local_procs) {
+
+			/* Don't overflow the line. col 57 is where the command starts */
+			strncpy(name, local_procs[i].name, term_width-57);
+			name[term_width-57] = '\0';
+			printf("%5d   %08x   %7d  %7d  %7d  %7d   %s \033[K\n", // esc[K erases to EOL
+			       local_procs[i].pid,
+			       local_procs[i].lockspace,
+			       display_type == DISPLAY_ACCUM ? local_procs[i].locks:local_procs[i].locks-local_procs[i].last_locks,
+			       display_type == DISPLAY_ACCUM ? local_procs[i].unlocks:local_procs[i].unlocks-local_procs[i].last_unlocks,
+			       display_type == DISPLAY_ACCUM ? local_procs[i].convs:local_procs[i].convs-local_procs[i].last_convs,
+			       local_procs[i].resources,
+			       name);
+		}
+	}
+}
+
+static void display_remote_procs(void)
+{
+	int num_procs = term_height/2 - 5;
+	int i;
+
+	printf("  pid   lockspace    locks   unlcks    convs      res   node\n");
+
+	for (i=0; i<num_procs; i++) {
+		if (i < num_remote_procs) {
+			struct in_addr addr;
+			addr.s_addr = remote_procs[i].nodeaddr;
+			printf("%5d   %08x   %7d  %7d  %7d  %7d   %s\033[K\n", // esc[K erases to EOL
+			       remote_procs[i].pid,
+			       remote_procs[i].lockspace,
+			       display_type == DISPLAY_ACCUM ? remote_procs[i].locks:remote_procs[i].locks-remote_procs[i].last_locks,
+			       display_type == DISPLAY_ACCUM ? remote_procs[i].unlocks:remote_procs[i].unlocks-remote_procs[i].last_unlocks,
+			       display_type == DISPLAY_ACCUM ? remote_procs[i].convs:remote_procs[i].convs-remote_procs[i].last_convs,
+			       remote_procs[i].resources,
+			       inet_ntoa(addr)); // TODO name
+		}
+	}
+}
+
+
+static void display(int ignored)
+{
+	char timebuf[1024];
+	time_t t;
+	struct tm *now;
+
+	/* Keep them in order */
+	sort_proc(remote_procs);
+	sort_proc(local_procs);
+
+	time(&t);
+	now = localtime(&t);
+	strftime(timebuf, sizeof(timebuf), "%d-%b-%Y  %H:%M:%S", now);
+
+
+	printf("\033[H\033[2Jdlmtop %s     %s ", VERSION, timebuf);
+	printf("\n");
+	printf("\nSeen %d packets, %d locals, %d remote,  %d non-dlm, %d errors\n", packets, l_locks, r_locks, nondlm, errors);
+	printf("\n");
+
+	printf("Local\n");
+	display_local_procs();
+	printf("\033[%d;0H", term_height/2+2);
+	printf("\nRemote\n");
+	display_remote_procs();
+
+	/* If we are showing deltas, then update: */
+	if (display_type == DISPLAY_DELTA) {
+		update_deltas(remote_procs, num_remote_procs);
+		update_deltas(local_procs, num_local_procs);
+	}
+
+	fflush(stdout);
+	alarm(interval);
+}
+
+static void new_term_size(int sig)
+{
+	struct winsize w;
+	int st;
+
+	st = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+	if (st)
+	{
+		perror("ioctl on stdout");
+		term_width = 80;
+		term_height = 24;
+	}
+	else
+	{
+		term_width = w.ws_col;
+		term_height = w.ws_row;
+	}
+	display(0);
+}
+
+
+static u_char save_buffer[4096]; /* more than enough for two packets */
+static int save_len = 0;
+
+/* Process a packet */
+static void my_callback(u_char *useless,const struct pcap_pkthdr* pkthdr,
+			const u_char* packet)
+{
+	struct dlm_message *req;
+	int offset = 0x42;    /* Skip past the TCP/IP header */
+	int ip_offset = 0x1A; /* Location of the local IP address */
+	int len;
+	struct locking_process *proc = NULL;
+	struct locking_process *proc_array;
+	unsigned int ip_addr;
+
+	memcpy(&ip_addr, packet+ip_offset, sizeof(ip_addr));
+
+	if (save_len) {
+		if (debug>1)
+			fprintf(stderr, "CC: Brought forward %d bytes. pktlen=%d\n", save_len, pkthdr->caplen);
+		memcpy(save_buffer+save_len, packet+offset, pkthdr->caplen-offset);
+		packet = save_buffer;
+		len = pkthdr->caplen + save_len - offset;
+		save_len = 0;
+		offset = 0;
+	}
+	else {
+		len = pkthdr->caplen;
+		memset(save_buffer, 0, sizeof(save_buffer));
+	}
+
+	packets++;
+	while (offset < len) {
+		req = (struct dlm_message *)((char *)packet+offset);
+
+		/* The packet remaining is too small to be of use */
+		if (len - offset < offsetof(struct dlm_message, m_length))
+			return;
+
+		/* Ignore error packets */
+		if (req->m_length <= 0 || req->m_length > 200) {
+			if (debug)
+				fprintf(stderr, "CC: error packet length = %d\n", req->m_length);
+			errors++;
+			save_len = 0;
+			return;
+		}
+
+		/* Non-dlm packet */
+		if (req->m_version != DLM_VERSION) {
+			if (debug)
+				fprintf(stderr, "CC: error non-dlm version = %x\n", req->m_version);
+			nondlm++;
+			return;
+		}
+
+		/* Carry forward incomplete packets */
+		if (offset+req->m_length > len) {
+			save_len = len - offset;
+			memcpy(save_buffer, packet+offset, save_len);
+			if (debug>1)
+				fprintf(stderr, "CC: Carried forward %d bytes. pktlen=%d, reqlen=%d, offset=%d\n", save_len, len, req->m_length, offset);
+			return;
+		}
+
+		/* Is it a local or remote packet ? */
+		if (is_local_ip(ip_addr)) {
+			proc_array = local_procs;
+			l_locks++;
+		}
+		else {
+			proc_array = remote_procs;
+			r_locks++;
+		}
+
+		if (debug > 2)
+			fprintf(stderr, "%s PACKET (IP:%x): cmd: %d, pid: %d\n", proc_array==&local_procs[0]?"Local ":"Remote", ip_addr, req->m_cmd, req->m_pid);
+
+		if (req->m_cmd == DLM_MSG_LOCK ||
+		    req->m_cmd == DLM_MSG_UNLOCK ||
+		    req->m_cmd == DLM_MSG_CONVERT) {
+
+			/* Not sure what's happening here */
+			if (req->m_pid > 32768) {
+				if (debug)
+					fprintf(stderr, "CC: odd pid %d seen. set to zero\n", req->m_pid);
+				req->m_pid = 0; // ???
+			}
+
+			proc = find_proc(proc_array, req->m_pid);
+
+			/* If this is a new struct, fill in the details */
+			if (!proc->pid) {
+				proc->pid = req->m_pid;
+				proc->lockspace = req->m_lockspace;
+				if (proc_array == &local_procs[0]) {
+					strcpy(proc->name, get_command(req->m_pid));
+				}
+				proc->nodeaddr = ip_addr;
+			}
+
+			switch (req->m_cmd) {
+			case DLM_MSG_LOCK:
+				proc->locks++;
+				proc->resources++;
+				break;
+			case DLM_MSG_UNLOCK:
+				proc->unlocks++;
+				if (proc->resources > 0)
+					proc->resources--;
+				break;
+			case DLM_MSG_CONVERT:
+				proc->convs++;
+				break;
+			default:
+				proc->others++;
+				break;
+			}
+		}
+		offset += req->m_length;
+	}
+}
+
+int main(int argc,char **argv)
+{
+    char *dev;
+    char errbuf[PCAP_ERRBUF_SIZE];
+    pcap_t* descr;
+    struct bpf_program fp;      /* hold compiled program     */
+    signed char opt;
+    struct winsize w;
+    int st;
+
+    /* Deal with command-line arguments */
+    opterr = 0;
+    optind = 0;
+    while ((opt=getopt(argc,argv,"?hVrladi:")) != EOF)
+    {
+	switch(opt)
+	{
+	case 'h':
+//		usage(argv[0], stdout);
+		exit(0);
+	case 'V':
+		fprintf(stderr, "dlmtop version 0.1\n");
+		exit(0);
+	case 'r':
+		sort_type = SORT_RESOURCES;
+		break;
+	case 'l':
+		sort_type = SORT_LOCKS;
+		break;
+	case 'a':
+		display_type = DISPLAY_ACCUM;
+		break;
+	case 'i':
+		interval = atoi(optarg);
+		break;
+	case 'd':
+		debug++;
+		break;
+	}
+    }
+
+    signal(SIGALRM, display);
+
+    /* Get (and maintain) terminal size */
+    signal(SIGWINCH, new_term_size);
+    st = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+    if (st)
+    {
+            perror("ioctl on stdout");
+            term_width = 80;
+	    term_height = 24;
+    }
+    else
+    {
+            term_width = w.ws_col;
+	    term_height = w.ws_row;
+    }
+
+    memset(local_procs, 0, sizeof(local_procs));
+    memset(remote_procs, 0, sizeof(remote_procs));
+
+    /* grab a device to peak into... */
+    dev = pcap_lookupdev(errbuf);
+    if(dev == NULL)
+    { fprintf(stderr,"%s\n",errbuf); exit(1); }
+
+    /* open device for reading this time lets set it in promiscuous
+     * mode so we can monitor traffic to another machine             */
+    descr = pcap_open_live(dev,65536,1,-1,errbuf);
+    if(descr == NULL)
+    { printf("pcap_open_live(): %s\n",errbuf); exit(1); }
+
+    /* Lets try and compile the program.. non-optimized */
+    if(pcap_compile(descr,&fp,dlm_filter,0,-1) == -1)
+    { fprintf(stderr,"Error calling pcap_compile\n"); exit(1); }
+
+    /* set the compiled program as the filter */
+    if(pcap_setfilter(descr,&fp) == -1)
+    { fprintf(stderr,"Error setting filter\n"); exit(1); }
+
+    /* ... and loop */
+    gettimeofday(&starttime, NULL);
+    alarm(interval);
+    fprintf(stderr, "Listening for DLM packets ...\n");
+    pcap_loop(descr,-1,my_callback,NULL);
+
+    return 0;
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]