[RFC]: Feature to display local arguments and variables

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi
We have implemented this piece of code to provide, crash tool, capability to display local variables and arguments. We would kindly request you to provide your feedback and guidance on this code so that we can take it further

How to Build/Requirements
1. Enable compiling extends option in main Makefile
2. create a symbolic link to netdump.h under extensions/
3. This feature makes use of libelf and libdw libraries provided by elfutils package. 4, This feature is implemented as an extend, local.so library needs to be loaded once crash prompt is available

Features
1. Currently it has feature to display arguments/locals of top most stack frame. ex usage: local params or local locals
2. Currently code can analyze only ppc64 dumps
3. It displays the current address of the local variables ( and some times direct values when the variables are stored in register and not in stack frame) 4. In case of code optimization, variable information is not available at that time printing "Failed to fetch information" (This is in accordance with gdb out put)

TBD
1. Stack unwinding code is still need to be implemented
2. Support for x86 dumps is still not provided
3. Context switching via -r command still needs to implemented

Note: We were planning to display the variable values instead of addresses as we are doing currently, but it adds up to additional challenges in terms of typecasting the variables, in case it is array or structure, so for the time being we are printing only address and expect user to dump contents using rd command. Suggestions here will be very helpful.

Attaching the code...but the Code is not extensively tested
please let us know your thoughts

Thanks
Sharyathi Nagesh



Index: crash-4.0-7.7/extensions/local.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ crash-4.0-7.7/extensions/local.c	2009-04-16 02:08:00.000000000 +0600
@@ -0,0 +1,541 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libdw.h>    
+#include <dwarf.h>
+#include "defs.h"    /* From the crash source top-level directory */
+#include "netdump.h"
+  
+/* Main Function */
+void cmd_local();        
+
+/* Initializations functions */
+static int initial_setup(void); /* Does the initial Dwarf setup */ 
+static int open_file(void);	 /* Returns the file descriptor for the Executable */
+static int initialize_local();
+static int initialize_register();
+static ulong fetch_register(int reg);
+
+/* x86 arch specific code */
+static void get_kdump_regs_x86();
+static ulong fetch_register_x86(int reg);
+
+/* ppc64 arch specific code */
+static void get_kdump_regs_ppc64();
+static ulong fetch_register_ppc64(int reg);
+
+/* Dwarf routines */
+static void print_function_variables();
+static void print_variables(Dwarf_Die *);
+static void print_variable(Dwarf_Die *, Dwarf_Die *);
+static int get_function_dies(Dwarf_Die **, int *);
+static ulong translate(Dwarf_Op *, size_t , ulong *,bool *);
+static Dwarf_Op * get_location(Dwarf_Attribute *, Dwarf_Addr , size_t *);
+static ulong variable_address(Dwarf_Die *, Dwarf_Die *, ulong *);
+
+/* generic functions */
+static int str_to_option(char *str);
+//static int display(void);
+static void read_vmcore(ulong , size_t );
+
+static Elf *elf_descriptor;
+static Dwarf *dbg;
+static int fd;
+char *help_local[];
+
+#define DISPLAY_ARGS		0x00000001
+#define DISPLAY_LOCALS		0x00000002
+#define STACK_UNWIND_UP 	0x00000004
+#define STACK_UNWIND_DOWN 	0x00000008
+
+
+struct local_context {
+	struct task_context *tc;
+	struct ppc64_pt_regs *regs;
+	Dwarf_Addr pc;
+	ulong flags;
+};
+
+struct local_context *local = NULL;
+static struct command_table_entry command_table[] = {
+         { "local", cmd_local, help_local, 0 },       /* One or more commands, */
+         { NULL }                                   /* terminated by NULL, */
+};
+
+/* 
+ *  The _init() function is called if the shared object is loaded. 
+ *  If desired, performs initialization here. 
+ */
+int
+_init() /* Register the command set. */
+{
+	fprintf(fp, "Calling _init()"); 
+        register_extension(command_table);
+	if (initial_setup() && initialize_local())
+		return 1;
+	else
+		return -1;
+}
+
+static int 
+initialize_local()
+{
+	if(local)
+		free(local);
+	local = calloc(1, sizeof(struct local_context));
+	local->tc = CURRENT_CONTEXT();
+	if(KDUMP_DUMPFILE() || NETDUMP_DUMPFILE())
+		initialize_register();
+	else
+		error(FATAL, 
+		 "Support for only KDUMP/NETDUMPs is available");
+		
+	return 1;
+} 
+
+static int initialize_register()
+{
+	int e_machine;
+	extern struct vmcore_data *nd;
+	if (nd->elf32)
+		e_machine = nd->elf32->e_machine;
+	else if(nd->elf64)
+		e_machine = nd->elf64->e_machine;
+	else
+		e_machine = EM_NONE;
+
+	switch(e_machine)
+	{
+	case EM_386:
+		get_kdump_regs_x86();
+		break;
+	case EM_PPC64:
+		get_kdump_regs_ppc64();
+		break;
+	default:
+		error(FATAL, 
+		 "Support for ELF machine type %d not available",
+		 e_machine);
+	}
+	return 0;
+}
+
+static void get_kdump_regs_x86()
+{
+
+}
+
+static void get_kdump_regs_ppc64()
+{
+	Elf64_Nhdr *note;
+	size_t len;
+	struct ppc64_pt_regs *pt_regs;
+	extern struct vmcore_data *nd;
+	if ((local->tc->task == tt->panic_task) ||
+		(is_task_active(local->tc->task) && nd->num_prstatus_notes > 1)) {
+		/*	
+		 * Registers are saved during the dump process for the 
+		 * panic task. Whereas in kdump, regs are captured for all 
+		 * CPUs if they responded to an IPI.
+		 */
+                if (nd->num_prstatus_notes > 1) {
+			if (local->tc->processor >= nd->num_prstatus_notes)
+				error(FATAL, 
+		          	    "cannot determine NT_PRSTATUS ELF note "
+				    "for %s task: %lx\n", 
+					(local->tc->task == tt->panic_task) ?
+					"panic" : "active", local->tc->task);	
+                        note = (Elf64_Nhdr *)
+                                nd->nt_prstatus_percpu[local->tc->processor];
+		} else
+			note = (Elf64_Nhdr *)nd->nt_prstatus;
+
+		len = sizeof(Elf64_Nhdr);
+		len = roundup(len + note->n_namesz, 4);
+		local->regs = (void *)((char *)note + len + 
+			MEMBER_OFFSET("elf_prstatus", "pr_reg"));
+	}
+	pt_regs = (struct ppc64_pt_regs *)local->regs;	
+	local->pc = (Dwarf_Addr) pt_regs->nip;
+}
+
+int 
+static initial_setup(void)
+{
+    	Elf_Cmd elf_cmd = ELF_C_READ;
+	Dwarf_Cmd dwarf_cmd = DWARF_C_READ;
+	int fd;
+	
+	if (elf_version(EV_CURRENT) == EV_NONE) {
+		error(FATAL, 
+		 "libelf.a our of date \n");
+	}
+
+	fd = open_file();
+	if (fd == -1)
+		return -1;
+	
+	elf_descriptor = elf_begin(fd,  elf_cmd, (Elf *) 0);
+	dbg = dwarf_begin_elf(elf_descriptor, dwarf_cmd, NULL);
+	if (!dbg) {
+		error(FATAL, 
+		 "Failed to get dwarf header error %s",
+		 dwarf_errmsg(dwarf_errno()));
+	}
+	/* Allocate one time memory to local */
+	return 1;
+}
+
+/*
+ *  Reads the vmlinux exe file and returns a file descriptor to calling function
+ */
+static int
+open_file()
+{
+	extern struct program_context *pc;
+
+	if (pc->namelist_debug)
+		fd = open(pc->namelist_debug, O_RDONLY);
+	else
+		fd = open(pc->namelist, O_RDONLY);
+	return fd;
+}
+
+/* 
+ *  The _fini() function is called if the shared object is unloaded. 
+ *  If desired, performs cleanups here. 
+ */
+int
+_fini() { 
+		elf_end(elf_descriptor);
+		close(fd);
+		return 0;
+	}
+
+/* 
+ * This function displays the local variables and argumen information 
+ * It takes input like 1. Function Address
+ * 2. Flag indicating whether local arguments or functions needs to be dumped
+ */
+void
+cmd_local()
+{
+        int c;
+	extern struct program_context *pc;
+		while ((c = getopt(argcnt, args, "pr")) != EOF) {
+                switch(c)
+                {
+		case 'p': 
+			/* Not implemented */
+			local->tc->pid = htol(optarg,FAULT_ON_ERROR, NULL);
+			initialize_local();
+			break;
+		case 'r':
+			/* Not implemented */
+			local->tc->pid = 0;
+			initialize_local();
+                default:
+                        argerrs++;
+                        break;
+                }
+        }
+	if(argerrs)
+		cmd_usage(pc->curcmd,SYNOPSIS); 
+	while(args[optind]){
+		switch(str_to_option(args[optind]))
+		{
+		case STACK_UNWIND_UP:
+			/* Not implemented */
+			fprintf(fp, "stack unwinding up");
+			break;
+		case STACK_UNWIND_DOWN:
+			/* Not implemented */
+			fprintf(fp, "stack unwinding down");
+			break;
+		case DISPLAY_LOCALS:
+			fprintf(fp, "display locals");
+			local->flags |= DISPLAY_LOCALS;
+			break;
+		case DISPLAY_ARGS:
+			fprintf(fp, "display args");
+			local->flags |= DISPLAY_ARGS;
+			break;
+		default:
+			cmd_usage(pc->curcmd,SYNOPSIS); 
+		}
+		++optind;
+	}
+	print_function_variables();
+}
+
+static int 
+str_to_option(char *str)
+{
+	if(STREQ(str,"params"))
+		return DISPLAY_ARGS;
+	else if(STREQ(str, "locals"))
+		return DISPLAY_LOCALS;
+	else if(STREQ(str, "up"))
+		return STACK_UNWIND_UP;
+	else if(STREQ(str,"down"))
+		return STACK_UNWIND_DOWN;
+	else
+		return 0;
+}
+
+static void 
+print_function_variables() 
+{
+	Dwarf_Die *scopes;
+	int nscopes;
+	int index;
+	
+	scopes = NULL;
+	get_function_dies(&scopes,&nscopes);
+	for(index = 0; index < nscopes; index++){
+		switch(dwarf_tag(&scopes[index])){
+		case DW_TAG_subprogram:
+		case DW_TAG_entry_point:
+		case DW_TAG_inlined_subroutine:
+			print_variables(&scopes[index]);
+			break;
+		default:
+			break;
+		}	
+	}
+}
+
+static int 
+get_function_dies(Dwarf_Die **scopes, int *nscopes)
+{
+	Dwarf_Die result_cu;
+	Dwarf_Die *cu_die;
+	Dwarf_Addr addr = local->pc;
+	
+	cu_die = dwarf_addrdie(dbg, addr, &result_cu);
+	if (!cu_die)
+		error(FATAL, 
+		 "unabled to get the compiler Unit for the address: %p", addr);
+	*nscopes = dwarf_getscopes(cu_die, addr, scopes);
+	if(!(*nscopes))
+		error(FATAL, 
+		 "unabled to get the function die at address %p", addr);
+	return 0;
+}
+
+static void 
+print_variables(Dwarf_Die *fun_die)
+{
+	Dwarf_Die result;
+	if (dwarf_child (fun_die, &result) == 0)
+	do{
+		 switch (dwarf_tag (&result))
+                 {
+                	case DW_TAG_variable:
+				if(local->flags & DISPLAY_LOCALS) 
+					print_variable(fun_die, &result);
+				break;
+	               	case DW_TAG_formal_parameter:
+				if(local->flags & DISPLAY_ARGS)
+					print_variable(fun_die, &result);
+				break;
+                  	default:
+				break;
+		}
+	}while(dwarf_siblingof(&result,&result) == 0);
+}
+
+static void 
+print_variable(Dwarf_Die *fun_die, Dwarf_Die *var_die)
+{
+	size_t size;
+	ulong var_addr;
+	ulong value = 0;
+	fprintf(fp,"\n %s\t", dwarf_diename(var_die));
+	if((long)(var_addr = variable_address (fun_die,var_die, &value)) < 0)
+		return;
+	if(!value)
+		read_vmcore(var_addr,size);
+	else
+		fprintf(fp,"\t value:%lx",value);
+}
+
+static void
+read_vmcore(ulong addr, size_t size)
+{
+	fprintf(fp,"\t address:%lx", addr);
+}
+
+static ulong
+variable_address(Dwarf_Die *fun_die, Dwarf_Die *var_die, ulong *value)
+{
+	size_t len;
+	Dwarf_Op *locexpr;
+	bool need_fb;
+	Dwarf_Attribute fb_attr, loc_attr;
+	Dwarf_Addr addr = local->pc;
+	ulong var_addr = 0x0;
+	if(!dwarf_attr_integrate(var_die, DW_AT_location, &loc_attr)){
+		fprintf(fp, "\t Failed to fetch location information");
+		goto out;
+	}	
+	locexpr = get_location(&loc_attr, addr, &len);
+	if (!locexpr) {
+		fprintf(fp, "\t Failed to fetch location");
+		goto out;
+	}
+	
+	var_addr = translate(locexpr, len, value, &need_fb);
+	if (need_fb) {
+		if(!dwarf_attr_integrate(fun_die, DW_AT_frame_base, &fb_attr)){
+			fprintf(fp, "\t Failed to fetch Function Frame Buffer");
+			goto out;
+		}
+
+		locexpr = get_location(&fb_attr, addr, &len);
+		if (!locexpr) {
+			fprintf(fp, "\t Failed to fetch frame base");
+			goto out;
+		}
+		var_addr += translate(locexpr, len, value, 0);			
+	}
+	return var_addr;
+	out:
+		return -1;
+}
+
+
+static ulong 
+translate(Dwarf_Op *locexpr, size_t len, ulong *value ,bool *need_fb)
+{
+	int i;
+	unsigned int reg;
+	unsigned long loc = 0;
+	unsigned long offset = 0;
+	for (i=0; i<len; i++) {
+	switch (locexpr[i].atom) {
+			case DW_OP_reg0 ... DW_OP_reg31:
+	  			reg = locexpr[i].atom - DW_OP_reg0;
+				goto op_reg;
+			case DW_OP_regx:
+				reg = locexpr[i].number;
+			op_reg:
+				*value = fetch_register(reg);
+				break;
+			case DW_OP_fbreg:
+				*need_fb = true;
+				loc = locexpr[i].number;
+	  			break;
+			case DW_OP_addr:
+				loc = locexpr[i].number;
+				break;
+			case DW_OP_breg0 ... DW_OP_breg31:
+				reg = locexpr[i].atom - DW_OP_breg0;
+				offset = locexpr[i].number;
+				loc = fetch_register(reg) + offset;
+				break;
+			case DW_OP_bregx:
+				reg = locexpr[i].number;
+				offset = locexpr[i].number2;
+				loc = fetch_register(reg) + offset;
+				break;
+			default:
+				error(FATAL, 
+				 "unprocessed OpCode in translate()");
+				break;
+		}
+	}
+	return loc;
+}
+
+static ulong 
+fetch_register(int reg)
+{
+	int e_machine;
+	extern struct vmcore_data *nd;
+	if (nd->elf32)
+		e_machine = nd->elf32->e_machine;
+	else if(nd->elf64)
+		e_machine = nd->elf64->e_machine;
+	else
+		e_machine = EM_NONE;
+
+	switch(e_machine)
+	{
+	case EM_386:
+		return fetch_register_x86(reg);
+	case EM_PPC64:
+		return fetch_register_ppc64(reg);
+	default:
+		error(FATAL, 
+		 "Fetch Registr support for ELF machine type %d not available",
+		 e_machine);
+	}
+	return 0;
+}
+
+static ulong 
+fetch_register_x86(int reg)
+{
+	return 0;
+}
+
+static ulong 
+fetch_register_ppc64(int reg)
+{
+	struct ppc64_pt_regs *regs;
+	regs = (struct ppc64_pt_regs *)local->regs;
+	return (ulong)regs->gpr[reg];
+}
+
+static Dwarf_Op *
+get_location(Dwarf_Attribute *location, Dwarf_Addr addr, size_t *len)
+{
+	Dwarf_Op *expr;
+	switch(dwarf_getlocation_addr(location, addr, &expr,len,1))
+	{
+	case 1:
+		if( len > 0)
+		break;
+	case 0:
+		return NULL;
+		break;
+	default:
+		return NULL;
+	}
+	return expr;
+}
+
+
+/* 
+ *    NAME
+ *      local - Displays local variables
+ *
+ *    SYNOPSIS
+ *      local params locals ...
+ *
+ *    DESCRIPTION
+ *      This command displays local variables for a functions.
+ *
+ *    EXAMPLE
+ *
+ *        crash> local  <locals|params>
+ *
+ */
+ 
+char *help_local[] = {
+        "local",                        /* command name */
+        "displays local variables in current context", /* short description */
+        "params locals...",                      /* arguments for the command */
+        "  This command displays local variables as well as parameters.",
+        "\nEXAMPLE",
+        "    crash> local  <locals|params>",
+        "    crash> local  -r ", /* Changes to current CPU context */
+        "    crash> local  <up|down> ", /* Unwinds the stack frame */
+        NULL
+};
+
Index: crash-4.0-7.7/extensions/local.mk
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ crash-4.0-7.7/extensions/local.mk	2009-04-16 01:48:24.000000000 +0600
@@ -0,0 +1,14 @@
+#
+ifeq ($(TARGET), PPC64)
+        TARGET_FLAGS = -D$(TARGET) -m64
+else
+        TARGET_FLAGS = -D$(TARGET)
+endif
+
+all: local.so
+
+local.so:
+	gcc -nostartfiles -shared -rdynamic -o $@ local.c -fPIC -ldw -L ../../elfutils-0.137/libdw -I ../../elfutils-0.137/libdw -I ../../elfutils-0.137/libelf/ -D$(TARGET) $(TARGET_CFLAGS) $(ADD_CFLAGS) -Wall; 
+
+clean:
+	make clean
Index: crash-4.0-7.7/netdump.c
===================================================================
--- crash-4.0-7.7.orig/netdump.c	2009-02-05 22:13:43.000000000 +0600
+++ crash-4.0-7.7/netdump.c	2009-04-15 16:44:30.000000000 +0600
@@ -18,8 +18,8 @@
 #include "defs.h"
 #include "netdump.h"
 
-static struct vmcore_data vmcore_data = { 0 };
-static struct vmcore_data *nd = &vmcore_data;
+struct vmcore_data vmcore_data = { 0 };
+struct vmcore_data *nd = &vmcore_data;
 static struct xen_kdump_data xen_kdump_data = { 0 };
 static void netdump_print(char *, ...);
 static void dump_Elf32_Ehdr(Elf32_Ehdr *);
--
Crash-utility mailing list
Crash-utility@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/crash-utility

[Index of Archives]     [Fedora Development]     [Fedora Desktop]     [Fedora SELinux]     [Yosemite News]     [KDE Users]     [Fedora Tools]

 

Powered by Linux