Merge changes Icedbb451,I57fac759,I2734a701,Ib0764206,Ic588b205, ...

* changes:
  Merge remote-tracking branch 'strace/master' into HEAD
  xtensa: sort values in struct_user_offsets
  Document -k option as experimental
  tests: robustify -k test
  tests: enhance -k test
  unwind: ignore memory mappings that have no PROT_EXEC bit set
  unwind: cleanup build_mmap_cache
  unwind: remove unused field from mmap_cache_t
  unwind: refactor stacktrace_walk
  unwind: constify binary_filename and symbol_name functions arguments
  unwind: disable stack trace with multiple personalities
diff --git a/NEWS b/NEWS
index 3347236..a311c34 100644
--- a/NEWS
+++ b/NEWS
@@ -6,7 +6,7 @@
     added --enable-arm-oabi option to enable it at configure time.
 
 * Improvements
-  * Added -k option to print stack trace after each traced syscall.
+  * Added experimental -k option to print stack trace after each traced syscall.
   * Added -w option to produce stats on syscall latency.
     (addresses Debian bug #457497).
   * Added ARC architecture support.
diff --git a/process.c b/process.c
index a880f9e..2cd0f01 100644
--- a/process.c
+++ b/process.c
@@ -1865,6 +1865,23 @@
 	{ 4*33, "sr" },
 #endif
 #ifdef XTENSA
+	{ REG_A_BASE,           "a0"            },
+	{ REG_A_BASE+1,         "a1"            },
+	{ REG_A_BASE+2,         "a2"            },
+	{ REG_A_BASE+3,         "a3"            },
+	{ REG_A_BASE+4,         "a4"            },
+	{ REG_A_BASE+5,         "a5"            },
+	{ REG_A_BASE+6,         "a6"            },
+	{ REG_A_BASE+7,         "a7"            },
+	{ REG_A_BASE+8,         "a8"            },
+	{ REG_A_BASE+9,         "a9"            },
+	{ REG_A_BASE+10,        "a10"           },
+	{ REG_A_BASE+11,        "a11"           },
+	{ REG_A_BASE+12,        "a12"           },
+	{ REG_A_BASE+13,        "a13"           },
+	{ REG_A_BASE+14,        "a14"           },
+	{ REG_A_BASE+15,        "a15"           },
+	{ REG_PC,               "pc"            },
 	{ SYSCALL_NR,           "syscall_nr"    },
 	{ REG_AR_BASE,          "ar0"           },
 	{ REG_AR_BASE+1,        "ar1"           },
@@ -1937,23 +1954,6 @@
 	{ REG_WB,               "wb"            },
 	{ REG_WS,               "ws"            },
 	{ REG_PS,               "ps"            },
-	{ REG_PC,               "pc"            },
-	{ REG_A_BASE,           "a0"            },
-	{ REG_A_BASE+1,         "a1"            },
-	{ REG_A_BASE+2,         "a2"            },
-	{ REG_A_BASE+3,         "a3"            },
-	{ REG_A_BASE+4,         "a4"            },
-	{ REG_A_BASE+5,         "a5"            },
-	{ REG_A_BASE+6,         "a6"            },
-	{ REG_A_BASE+7,         "a7"            },
-	{ REG_A_BASE+8,         "a8"            },
-	{ REG_A_BASE+9,         "a9"            },
-	{ REG_A_BASE+10,        "a10"           },
-	{ REG_A_BASE+11,        "a11"           },
-	{ REG_A_BASE+12,        "a12"           },
-	{ REG_A_BASE+13,        "a13"           },
-	{ REG_A_BASE+14,        "a14"           },
-	{ REG_A_BASE+15,        "a15"           },
 #endif
 
 	/* Other fields in "struct user" */
diff --git a/strace.1 b/strace.1
index c310d65..2a24c38 100644
--- a/strace.1
+++ b/strace.1
@@ -268,7 +268,7 @@
 Print the instruction pointer at the time of the system call.
 .TP
 .B \-k
-Print the execution stack trace of the traced processes after each system call.
+Print the execution stack trace of the traced processes after each system call (experimental).
 .TP
 .B \-q
 Suppress messages about attaching, detaching etc.  This happens
diff --git a/strace.c b/strace.c
index 46c9d63..4154cde 100644
--- a/strace.c
+++ b/strace.c
@@ -238,7 +238,7 @@
 -P path -- trace accesses to path\n\
 "
 #ifdef USE_LIBUNWIND
-"-k obtain stack trace between each syscall\n\
+"-k obtain stack trace between each syscall (experimental)\n\
 "
 #endif
 /* ancient, no one should use it
diff --git a/tests/Makefile.am b/tests/Makefile.am
index efe646d..922452a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -11,6 +11,8 @@
 	uio
 
 uio_CFLAGS = $(AM_CFLAGS) -D_FILE_OFFSET_BITS=64
+stack_fcall_SOURCES = stack-fcall.c \
+	stack-fcall-0.c stack-fcall-1.c stack-fcall-2.c stack-fcall-3.c
 
 TESTS = \
 	ptrace_setoptions.test \
diff --git a/tests/stack-fcall-0.c b/tests/stack-fcall-0.c
new file mode 100644
index 0000000..12a260d
--- /dev/null
+++ b/tests/stack-fcall-0.c
@@ -0,0 +1,6 @@
+int f1(int i);
+
+int f0(int i)
+{
+	return f1(i) - i;
+}
diff --git a/tests/stack-fcall-1.c b/tests/stack-fcall-1.c
new file mode 100644
index 0000000..8716702
--- /dev/null
+++ b/tests/stack-fcall-1.c
@@ -0,0 +1,6 @@
+int f2(int i);
+
+int f1(int i)
+{
+	return f2(i) + i;
+}
diff --git a/tests/stack-fcall-2.c b/tests/stack-fcall-2.c
new file mode 100644
index 0000000..19f8cf8
--- /dev/null
+++ b/tests/stack-fcall-2.c
@@ -0,0 +1,6 @@
+int f3(int i);
+
+int f2(int i)
+{
+	return f3(i) - i;
+}
diff --git a/tests/stack-fcall-3.c b/tests/stack-fcall-3.c
new file mode 100644
index 0000000..3af1667
--- /dev/null
+++ b/tests/stack-fcall-3.c
@@ -0,0 +1,6 @@
+#include <unistd.h>
+
+int f3(int i)
+{
+	return getpid() + i;
+}
diff --git a/tests/stack-fcall.c b/tests/stack-fcall.c
index 1c66fef..134d54f 100644
--- a/tests/stack-fcall.c
+++ b/tests/stack-fcall.c
@@ -1,23 +1,7 @@
-#include <unistd.h>
-#include <sys/types.h>
-
-/* Use "volatile" to avoid compiler optimization. */
-
-int f1(int i)
-{
-	static pid_t (* volatile g)(void) = getpid;
-	return g() + i;
-}
-
-int f0(volatile int i)
-{
-	static int (* volatile g)(int) = f1;
-	return g(i) - i;
-}
+int f0(int i);
 
 int main(int argc, char** argv)
 {
-	static int (* volatile g)(int) = f0;
-	g(argc);
+	f0(argc);
 	return 0;
 }
diff --git a/tests/strace-k.test b/tests/strace-k.test
index 3845c8c..f757fb9 100755
--- a/tests/strace-k.test
+++ b/tests/strace-k.test
@@ -23,7 +23,7 @@
 	fail_ "$STRACE $args failed"
 }
 
-expected='getpid f1 f0 main '
+expected='getpid f3 f2 f1 f0 main '
 result=$(sed -n '1,/(main+0x[a-f0-9]\+) .*/ s/^.*(\([^+]\+\)+0x[a-f0-9]\+) .*/\1/p' $LOG |
 	tr '\n' ' ')
 
diff --git a/unwind.c b/unwind.c
index ba7b579..5b06008 100644
--- a/unwind.c
+++ b/unwind.c
@@ -47,7 +47,7 @@
 struct mmap_cache_t {
 	/**
 	 * example entry:
-	 * 7fabbb09b000-7fabbb09f000 r--p 00179000 fc:00 1180246 /lib/libc-2.11.1.so
+	 * 7fabbb09b000-7fabbb09f000 r-xp 00179000 fc:00 1180246 /lib/libc-2.11.1.so
 	 *
 	 * start_addr  is 0x7fabbb09b000
 	 * end_addr    is 0x7fabbb09f000
@@ -57,16 +57,15 @@
 	unsigned long start_addr;
 	unsigned long end_addr;
 	unsigned long mmap_offset;
-	char* binary_filename;
-	bool deleted;
+	char *binary_filename;
 };
 
 /*
  * Type used in stacktrace walker
  */
 typedef void (*call_action_fn)(void *data,
-			       char *binary_filename,
-			       char *symbol_name,
+			       const char *binary_filename,
+			       const char *symbol_name,
 			       unw_word_t function_offset,
 			       unsigned long true_offset);
 typedef void (*error_action_fn)(void *data,
@@ -131,28 +130,22 @@
 /*
  * caching of /proc/ID/maps for each process to speed up stack tracing
  *
- * The cache must be refreshed after some syscall: mmap, mprotect, munmap, execve
+ * The cache must be refreshed after syscalls that affect memory mappings,
+ * e.g. mmap, mprotect, munmap, execve.
  */
 static void
 build_mmap_cache(struct tcb* tcp)
 {
-	unsigned long start_addr, end_addr, mmap_offset;
-	char filename[sizeof ("/proc/0123456789/maps")];
-	char buffer[PATH_MAX + 80];
-	char binary_path[PATH_MAX];
-	struct mmap_cache_t *cur_entry, *prev_entry;
+	FILE *fp;
+	struct mmap_cache_t *cache_head;
 	/* start with a small dynamically-allocated array and then expand it */
 	size_t cur_array_size = 10;
-	struct mmap_cache_t *cache_head;
-	FILE *fp;
+	char filename[sizeof("/proc/4294967296/maps")];
+	char buffer[PATH_MAX + 80];
 
-	const char *deleted = " (deleted)";
-	size_t blen;
-	size_t dlen;
+	unw_flush_cache(libunwind_as, 0, 0);
 
-	unw_flush_cache (libunwind_as, 0, 0);
-
-	sprintf(filename, "/proc/%d/maps", tcp->pid);
+	sprintf(filename, "/proc/%u/maps", tcp->pid);
 	fp = fopen_for_input(filename, "r");
 	if (!fp) {
 		perror_msg("fopen: %s", filename);
@@ -164,59 +157,60 @@
 		die_out_of_memory();
 
 	while (fgets(buffer, sizeof(buffer), fp) != NULL) {
-		binary_path[0] = '\0'; // 'reset' it just to be paranoid
+		struct mmap_cache_t *entry;
+		unsigned long start_addr, end_addr, mmap_offset;
+		char exec_bit;
+		char binary_path[PATH_MAX];
 
-		sscanf(buffer, "%lx-%lx %*c%*c%*c%*c %lx %*x:%*x %*d %[^\n]",
-		       &start_addr, &end_addr, &mmap_offset, binary_path);
-
-		/* ignore special 'fake files' like "[vdso]", "[heap]", "[stack]", */
-		if (binary_path[0] == '[') {
+		if (sscanf(buffer, "%lx-%lx %*c%*c%c%*c %lx %*x:%*x %*d %[^\n]",
+			   &start_addr, &end_addr, &exec_bit,
+			   &mmap_offset, binary_path) != 5)
 			continue;
-		}
 
-		if (binary_path[0] == '\0') {
+		/* ignore mappings that have no PROT_EXEC bit set */
+		if (exec_bit != 'x')
 			continue;
+
+		if (end_addr < start_addr) {
+			error_msg("%s: unrecognized file format", filename);
+			break;
 		}
 
-		if (end_addr < start_addr)
-			perror_msg_and_die("%s: unrecognized maps file format",
-					   filename);
-
-		cur_entry = &cache_head[tcp->mmap_cache_size];
-		cur_entry->start_addr = start_addr;
-		cur_entry->end_addr = end_addr;
-		cur_entry->mmap_offset = mmap_offset;
-		cur_entry->binary_filename = strdup(binary_path);
-
-		dlen = strlen(deleted);
-		blen = strlen(binary_path);
-		if (blen >= dlen && strcmp(binary_path + blen - dlen, deleted) == 0)
-			cur_entry->deleted = true;
-		else
-			cur_entry->deleted = false;
-
 		/*
 		 * sanity check to make sure that we're storing
 		 * non-overlapping regions in ascending order
 		 */
 		if (tcp->mmap_cache_size > 0) {
-			prev_entry = &cache_head[tcp->mmap_cache_size - 1];
-			if (prev_entry->start_addr >= cur_entry->start_addr)
-				perror_msg_and_die("Overlaying memory region in %s",
-						   filename);
-			if (prev_entry->end_addr > cur_entry->start_addr)
-				perror_msg_and_die("Overlaying memory region in %s",
-						   filename);
+			entry = &cache_head[tcp->mmap_cache_size - 1];
+			if (entry->start_addr == start_addr &&
+			    entry->end_addr == end_addr) {
+				/* duplicate entry, e.g. [vsyscall] */
+				continue;
+			}
+			if (start_addr <= entry->start_addr ||
+			    start_addr < entry->end_addr) {
+				error_msg("%s: overlapping memory region",
+					  filename);
+				continue;
+			}
 		}
-		tcp->mmap_cache_size++;
 
-		/* resize doubling its size */
 		if (tcp->mmap_cache_size >= cur_array_size) {
 			cur_array_size *= 2;
-			cache_head = realloc(cache_head, cur_array_size * sizeof(*cache_head));
+			cache_head = realloc(cache_head,
+					     cur_array_size * sizeof(*cache_head));
 			if (!cache_head)
 				die_out_of_memory();
 		}
+
+		entry = &cache_head[tcp->mmap_cache_size];
+		entry->start_addr = start_addr;
+		entry->end_addr = end_addr;
+		entry->mmap_offset = mmap_offset;
+		entry->binary_filename = strdup(binary_path);
+		if (!entry->binary_filename)
+			die_out_of_memory();
+		tcp->mmap_cache_size++;
 	}
 	fclose(fp);
 	tcp->mmap_cache = cache_head;
@@ -269,6 +263,12 @@
 void
 unwind_cache_invalidate(struct tcb* tcp)
 {
+#if SUPPORTED_PERSONALITIES > 1
+	if (tcp->currpers != DEFAULT_PERSONALITY) {
+		/* disable strack trace */
+		return;
+	}
+#endif
 	mmap_cache_generation++;
 	DPRINTF("tgen=%u, ggen=%u, tcp=%p, cache=%p", "increment",
 		tcp->mmap_cache_generation,
@@ -277,6 +277,82 @@
 		tcp->mmap_cache);
 }
 
+static void
+get_symbol_name(unw_cursor_t *cursor, char **name,
+		size_t *size, unw_word_t *offset)
+{
+	for (;;) {
+		int rc = unw_get_proc_name(cursor, *name, *size, offset);
+		if (rc == 0)
+			break;
+		if (rc != -UNW_ENOMEM) {
+			**name = '\0';
+			*offset = 0;
+			break;
+		}
+		*size *= 2;
+		*name = realloc(*name, *size);
+		if (!*name)
+			die_out_of_memory();
+	}
+}
+
+static int
+print_stack_frame(struct tcb *tcp,
+		  call_action_fn call_action,
+		  error_action_fn error_action,
+		  void *data,
+		  unw_cursor_t *cursor,
+		  char **symbol_name,
+		  size_t *symbol_name_size)
+{
+	unw_word_t ip;
+	int lower = 0;
+	int upper = (int) tcp->mmap_cache_size - 1;
+
+	if (unw_get_reg(cursor, UNW_REG_IP, &ip) < 0) {
+		perror_msg("Can't walk the stack of process %d", tcp->pid);
+		return -1;
+	}
+
+	while (lower <= upper) {
+		struct mmap_cache_t *cur_mmap_cache;
+		int mid = (upper + lower) / 2;
+
+		cur_mmap_cache = &tcp->mmap_cache[mid];
+
+		if (ip >= cur_mmap_cache->start_addr &&
+		    ip < cur_mmap_cache->end_addr) {
+			unsigned long true_offset;
+			unw_word_t function_offset;
+
+			get_symbol_name(cursor, symbol_name, symbol_name_size,
+					&function_offset);
+			true_offset = ip - cur_mmap_cache->start_addr +
+				cur_mmap_cache->mmap_offset;
+			call_action(data,
+				    cur_mmap_cache->binary_filename,
+				    *symbol_name,
+				    function_offset,
+				    true_offset);
+			return 0;
+		}
+		else if (ip < cur_mmap_cache->start_addr)
+			upper = mid - 1;
+		else
+			lower = mid + 1;
+	}
+
+	/*
+	 * there is a bug in libunwind >= 1.0
+	 * after a set_tid_address syscall
+	 * unw_get_reg returns IP == 0
+	 */
+	if(ip)
+		error_action(data, "unexpected_backtracing_error", ip);
+	return -1;
+}
+
 /*
  * walking the stack
  */
@@ -286,17 +362,10 @@
 		error_action_fn error_action,
 		void *data)
 {
-	unw_word_t ip;
-	unw_cursor_t cursor;
-	unw_word_t function_offset;
-	int stack_depth = 0, ret_val;
-	/* these are used for the binary search through the mmap_chace */
-	int lower, upper, mid;
+	char *symbol_name;
 	size_t symbol_name_size = 40;
-	char * symbol_name;
-	struct mmap_cache_t* cur_mmap_cache;
-	unsigned long true_offset;
-	bool berror_expected = false;
+	unw_cursor_t cursor;
+	int stack_depth;
 
 	if (!tcp->mmap_cache)
 		error_msg_and_die("bug: mmap_cache is NULL");
@@ -310,90 +379,16 @@
 	if (unw_init_remote(&cursor, libunwind_as, tcp->libunwind_ui) < 0)
 		perror_msg_and_die("Can't initiate libunwind");
 
-	do {
-		/* looping on the stack frame */
-		if (unw_get_reg(&cursor, UNW_REG_IP, &ip) < 0) {
-			perror_msg("Can't walk the stack of process %d", tcp->pid);
+	for (stack_depth = 0; stack_depth < 256; ++stack_depth) {
+		if (print_stack_frame(tcp, call_action, error_action, data,
+				&cursor, &symbol_name, &symbol_name_size) < 0)
 			break;
-		}
-
-		lower = 0;
-		upper = (int) tcp->mmap_cache_size - 1;
-
-		while (lower <= upper) {
-			/* find the mmap_cache and print the stack frame */
-			mid = (upper + lower) / 2;
-			cur_mmap_cache = &tcp->mmap_cache[mid];
-
-			if (ip >= cur_mmap_cache->start_addr &&
-			    ip < cur_mmap_cache->end_addr) {
-				for (;;) {
-					symbol_name[0] = '\0';
-					ret_val = unw_get_proc_name(&cursor, symbol_name,
-						symbol_name_size, &function_offset);
-					if (ret_val != -UNW_ENOMEM)
-						break;
-					symbol_name_size *= 2;
-					symbol_name = realloc(symbol_name, symbol_name_size);
-					if (!symbol_name)
-						die_out_of_memory();
-				}
-
-				if (cur_mmap_cache->deleted)
-					berror_expected = true;
-
-				true_offset = ip - cur_mmap_cache->start_addr +
-					cur_mmap_cache->mmap_offset;
-				if (symbol_name[0]) {
-					call_action(data,
-						    cur_mmap_cache->binary_filename,
-						    symbol_name,
-						    function_offset,
-						    true_offset);
-				} else {
-					call_action(data,
-						    cur_mmap_cache->binary_filename,
-						    symbol_name,
-						    0,
-						    true_offset);
-				}
-				break; /* stack frame printed */
-			}
-			else if (mid == 0) {
-				/*
-				 * there is a bug in libunwind >= 1.0
-				 * after a set_tid_address syscall
-				 * unw_get_reg returns IP == 0
-				 */
-				if(ip)
-					error_action(data,
-						     "backtracing_error", 0);
-				goto ret;
-			}
-			else if (ip < cur_mmap_cache->start_addr)
-				upper = mid - 1;
-			else
-				lower = mid + 1;
-
-		}
-		if (lower > upper) {
-			error_action(data,
-				     berror_expected
-				     ?"expected_backtracing_error"
-				     :"unexpected_backtracing_error",
-				     ip);
-			goto ret;
-		}
-
-		ret_val = unw_step(&cursor);
-
-		if (++stack_depth > 255) {
-			error_action(data,
-				     "too many stack frames", 0);
+		if (unw_step(&cursor) <= 0)
 			break;
-		}
-	} while (ret_val > 0);
-ret:
+	}
+	if (stack_depth >= 256)
+		error_action(data, "too many stack frames", 0);
+
 	free(symbol_name);
 }
 
@@ -427,8 +422,8 @@
 
 static void
 print_call_cb(void *dummy,
-	      char *binary_filename,
-	      char *symbol_name,
+	      const char *binary_filename,
+	      const char *symbol_name,
 	      unw_word_t function_offset,
 	      unsigned long true_offset)
 {
@@ -456,8 +451,8 @@
 }
 
 static char *
-sprint_call_or_error(char *binary_filename,
-		     char *symbol_name,
+sprint_call_or_error(const char *binary_filename,
+		     const char *symbol_name,
 		     unw_word_t function_offset,
 		     unsigned long true_offset,
 		     const char *error)
@@ -487,8 +482,8 @@
  */
 static void
 queue_put(struct queue_t *queue,
-	  char *binary_filename,
-	  char *symbol_name,
+	  const char *binary_filename,
+	  const char *symbol_name,
 	  unw_word_t function_offset,
 	  unsigned long true_offset,
 	  const char *error)
@@ -517,8 +512,8 @@
 
 static void
 queue_put_call(void *queue,
-	       char *binary_filename,
-	       char *symbol_name,
+	       const char *binary_filename,
+	       const char *symbol_name,
 	       unw_word_t function_offset,
 	       unsigned long true_offset)
 {
@@ -566,6 +561,12 @@
 void
 unwind_print_stacktrace(struct tcb* tcp)
 {
+#if SUPPORTED_PERSONALITIES > 1
+	if (tcp->currpers != DEFAULT_PERSONALITY) {
+		/* disable strack trace */
+		return;
+	}
+#endif
        if (tcp->queue->head) {
 	       DPRINTF("tcp=%p, queue=%p", "queueprint", tcp, tcp->queue->head);
 	       queue_print(tcp->queue);
@@ -582,6 +583,12 @@
 void
 unwind_capture_stacktrace(struct tcb *tcp)
 {
+#if SUPPORTED_PERSONALITIES > 1
+	if (tcp->currpers != DEFAULT_PERSONALITY) {
+		/* disable strack trace */
+		return;
+	}
+#endif
 	if (tcp->queue->head)
 		error_msg_and_die("bug: unprinted entries in queue");