From 9e9f6e9d2a75b08404551db9820186e37538511a Mon Sep 17 00:00:00 2001
From: Lei Wen <leiwen@marvell.com>
Date: Mon, 22 Oct 2012 11:29:05 +0800
Subject: [PATCH] GCORE: add directly show backtrace function

now with -t option, we could directly display the specified user
thread's backtrace without needing dump into a seperated core file, then
attach with gdb again to analysis

Change-Id: I5b100d23df0ea13b438f53adef29822cee6175d5
Signed-off-by: Lei Wen <leiwen@marvell.com>
---
 extensions/gcore.c                 |   30 ++++++++++++++-------
 extensions/gcore.mk                |    1 +
 extensions/libgcore/gcore_btshow.c |   50 ++++++++++++++++++++++++++++++++++++
 extensions/libgcore/gcore_defs.h   |    1 +
 gdb-7.3.1.patch                    |   19 +++++++++++++
 gdb_interface.c                    |    7 +++--
 6 files changed, 95 insertions(+), 13 deletions(-)
 create mode 100644 extensions/libgcore/gcore_btshow.c

diff --git a/extensions/gcore.c b/extensions/gcore.c
index 90ed0a1..ba654c9 100644
--- a/extensions/gcore.c
+++ b/extensions/gcore.c
@@ -21,7 +21,7 @@
 static void gcore_offset_table_init(void);
 static void gcore_size_table_init(void);
 
-static void do_gcore(char *arg);
+static void do_gcore(char *arg, int showbt);
 static void print_version(void);
 
 static struct command_table_entry command_table[] = {
@@ -181,7 +181,7 @@ void
 cmd_gcore(void)
 {
 	char c, *foptarg, *voptarg;
-	int optversion;
+	int optversion, showbt;
 
 	if (ACTIVE())
 		error(FATAL, "no support on live kernel");
@@ -192,7 +192,7 @@ cmd_gcore(void)
 	foptarg = voptarg = NULL;
 	optversion = FALSE;
 
-	while ((c = getopt(argcnt, args, "f:v:V")) != EOF) {
+	while ((c = getopt(argcnt, args, "f:v:VtT")) != EOF) {
 		switch (c) {
 		case 'V':
 			optversion = TRUE;
@@ -207,6 +207,12 @@ cmd_gcore(void)
 				goto argerr;
 			voptarg = optarg;
 			break;
+		case 't':
+			showbt = 0x2;
+			break;
+		case 'T':
+			showbt = 0x3;
+			break;
 		default:
 		argerr:
 			argerrs++;
@@ -249,12 +255,12 @@ cmd_gcore(void)
 	}
 
 	if (!args[optind]) {
-		do_gcore(NULL);
+		do_gcore(NULL, showbt);
 		return;
 	}
 
 	for (; args[optind]; optind++) {
-		do_gcore(args[optind]);
+		do_gcore(args[optind], showbt);
 		free_all_bufs();
 	}
 
@@ -279,7 +285,7 @@ cmd_gcore(void)
  * operation. Thus, it is necessary to call free_all_bufs() each time
  * calling do_gcore(). See the end of cmd_gcore().
  */
-static void do_gcore(char *arg)
+static void do_gcore(char *arg, int showbt)
 {
 	if (!setjmp(pc->foreach_loop_env)) {
 		struct task_context *tc;
@@ -308,12 +314,16 @@ static void do_gcore(char *arg)
 			(void) set_context(tc->task, NO_PID);
 		}
 
-		snprintf(gcore->corename, CORENAME_MAX_SIZE + 1, "core.%lu.%s",
-			 task_tgid(CURRENT_TASK()), CURRENT_COMM());
+		if (showbt)
+			gcore_btshow(showbt);
+		else {
+			snprintf(gcore->corename, CORENAME_MAX_SIZE + 1, "core.%lu.%s",
+					task_tgid(CURRENT_TASK()), CURRENT_COMM());
 
-		gcore_elf_init(gcore);
+			gcore_elf_init(gcore);
 
-		gcore_coredump();
+			gcore_coredump();
+		}
 	}
 
 	pc->flags &= ~IN_FOREACH;
diff --git a/extensions/gcore.mk b/extensions/gcore.mk
index 1eb743a..beac25d 100644
--- a/extensions/gcore.mk
+++ b/extensions/gcore.mk
@@ -47,6 +47,7 @@ ifeq ($(shell /bin/ls ../defs.h 2> /dev/null), ../defs.h)
 endif
 
 GCORE_CFILES = \
+	libgcore/gcore_btshow.c \
 	libgcore/gcore_coredump.c \
 	libgcore/gcore_coredump_table.c \
 	libgcore/gcore_dumpfilter.c \
diff --git a/extensions/libgcore/gcore_btshow.c b/extensions/libgcore/gcore_btshow.c
new file mode 100644
index 0000000..96dd612
--- /dev/null
+++ b/extensions/libgcore/gcore_btshow.c
@@ -0,0 +1,50 @@
+/* gcore_btshow.c -- core analysis suite
+ *
+ * Copyright (C) 2010, 2011 FUJITSU LIMITED
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.
+ */
+
+#include <defs.h>
+#include <gcore_defs.h>
+
+
+void gcore_btshow(int showbt)
+{
+	char *pr_reg_buf;
+	const struct user_regset_view *view = task_user_regset_view();
+	const uint tgid = task_tgid(CURRENT_TASK());
+	struct task_context *tc;
+	struct gnu_request *req;
+	int i;
+
+	tc = FIRST_CONTEXT();
+	for (i = 0; i < RUNNING_TASKS(); i++, tc++)
+		if (task_tgid(tc->task) == tgid)
+			break;
+
+	view->regsets[0].get(task_to_context(tc->task), &view->regsets[0],
+			view->regsets[0].size, pr_reg_buf);
+
+	req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
+	req->buf = pr_reg_buf;
+
+	req->command = GNU_DIS_FUNC_UNWIND;
+	req->value = showbt & 0x3;
+	req->flags |= GNU_RETURN_ON_ERROR;
+
+	gdb_interface(req);
+	if (req->flags & GNU_COMMAND_FAILED) {
+		FREEBUF(req);
+		error(FATAL, "display function unwind fail\n");
+	}
+	FREEBUF(req);
+}
diff --git a/extensions/libgcore/gcore_defs.h b/extensions/libgcore/gcore_defs.h
index 4b48bc6..ec48f1e 100644
--- a/extensions/libgcore/gcore_defs.h
+++ b/extensions/libgcore/gcore_defs.h
@@ -481,6 +481,7 @@ extern void gcore_coredump_table_init(void);
  */
 extern void gcore_coredump(void);
 
+extern void gcore_btshow(int showbt);
 /*
  * gcore_global_data.c
  */
diff --git a/gdb-7.3.1.patch b/gdb-7.3.1.patch
index df0a25f..534a85c 100644
--- a/gdb-7.3.1.patch
+++ b/gdb-7.3.1.patch
@@ -1995,3 +1995,22 @@ index 6e05ad1..58d5d26 100644
    /* Dispatch to the topmost target, not the flattened current_target.
       Memory accesses check target->to_has_(all_)memory, and the
       flattened target doesn't inherit those.  */
+--- gdb-7.3.1/gdb/symtab.c
++++ gdb-7.3.1/gdb/symtab.c
+@@ -5378,12 +5378,15 @@ gdb_dis_func_unwind(struct gnu_request *req)
+ 		return;
+ 	}
+ 
++	if (req->value & 0x2)
++		solib_add (NULL, 0, NULL, auto_solib_add);
++
+ 	fi = create_new_frame(sp,  pc);
+ 	current_frame = fi;
+ 
+ 	for (i = 0; fi; i ++, fi = get_prev_frame(fi)) {
+ 		print_frame_info(fi, 1, LOCATION, 1);
+-		if (req->value)
++		if (req->value & 0x1)
+ 			print_frame_local_vars (fi, 1, gdb_stdout);
+ 	}
+ }
diff --git a/gdb_interface.c b/gdb_interface.c
index 8e9cc66..330180c 100755
--- a/gdb_interface.c
+++ b/gdb_interface.c
@@ -697,7 +697,7 @@ static char *prohibited_list[] = {
 	"signal", "step", "s", "stepi", "target", "thread", "until", "delete", 
 	"clear", "disable", "enable", "condition", "ignore", "frame", 
 	"select-frame", "f", "up", "down", "catch", "tcatch", "return",
-	"file", "exec-file", "core-file", "symbol-file", "load", "si", "ni", 
+	"exec-file", "core-file", "symbol-file", "load", "si", "ni", 
 	"shell", 
 	NULL  /* must be last */
 };
@@ -827,8 +827,9 @@ gdb_readmem_callback(ulong addr, void *buf, int len, int write)
 	else if (pc->curcmd_flags & MEMTYPE_FILEADDR)
 		memtype = FILEADDR;
 	else if (!IS_KVADDR(addr)) {
-		if (STREQ(pc->curcmd, "gdb") && 
-		    STRNEQ(pc->cur_req->buf, "x/")) {
+		if ((STREQ(pc->curcmd, "gdb") && 
+		    STRNEQ(pc->cur_req->buf, "x/")) ||
+			STREQ(pc->curcmd, "gcore")) {
 			memtype = UVADDR;
 		} else {
 			if (CRASHDEBUG(1))
-- 
1.7.5.4

