[PATCH v2 1/5] unwind-libdw: add -kk/--stack-traces=source option

Masatake YAMATO yamato at redhat.com
Sun Nov 19 05:30:21 UTC 2023


This change adds source code information to stack traces printed with
-k option if strace links to libdw.

An example output:

    exit_group(0)                           = ?
    +++ exited with 0 +++
     > /usr/lib64/libc.so.6(_exit+0x1d) [0xdb44d] ../sysdeps/unix/sysv/linux/_exit.c:30
     > /usr/lib64/libc.so.6(__run_exit_handlers+0x125) [0x40165] /usr/src/debug/glibc-2.37-4.fc38.x86_64/stdlib/exit.c:134
     > /usr/lib64/libc.so.6(exit+0x1d) [0x402ed] /usr/src/debug/glibc-2.37-4.fc38.x86_64/stdlib/exit.c:141
     > /usr/lib64/libc.so.6(__libc_start_call_main+0x80) [0x27b50] ../sysdeps/nptl/libc_start_call_main.h:74
     > /usr/lib64/libc.so.6(__libc_start_main@@GLIBC_2.34+0x8a) [0x27c0a] ../csu/libc-start.c:360
     > /usr/bin/ls(_start+0x24) [0x6d54]

This change also adds --stack-traces=symbol option as an alias for the
original -k/--stack-traces options.

NOTE: If the target program is built with clang, -gdwarf-aranges option
must be specified in addition to -g for making -kk option work.
See https://sourceware.org/bugzilla/show_bug.cgi?id=30948 ahout this
limitation.

* NEWS: Mention this.
* doc/strace.1.in: Document this.
* src/defs.h (stack_trace_modes): New enum.
(stack_trace_modes::STACK_TRACE_OFF,STACK_TRACE_ON,
STACK_TRACE_WITH_SRCINFO): New enumerators.
(stack_trace_enabled): Rename this variabel declaration to...
(stack_trace_mode): ... this. The type is also changed from bool to
enum stack_trace_modes.
(unwind_init): Add a bool parameter.
* src/filter_seccomp.c (traced_by_seccomp): Use the new variable name,
stack_trace_mode.
* src/strace.c (stack_trace_enabled): Rename this variabel to...
(stack_trace_mode): ... this.
[ENABLE_STACKTRACE]/[USE_LIBDW] (K_OPT): Add "kk" option.
[ENABLE_STACKTRACE] (usage): Print --stack-traces=symbol options.
[ENABLE_STACKTRACE]/[USE_LIBDW] (usage): Print -kk and
--strack-traces=source options.
[ENABLE_STACKTRACE] (after_successful_attach): Use the new variable
name, stack_trace_mode.
(GETOPT_STACK): New enumerator.
(init::longopts): Let --stack-traces take an option argument. Use
GETOPT_STACK as the val for --stack-traces.
[ENABLE_STACKTRACE] (init): Handle -kk and --stack-traces=source
options.
(init): Pass ture to unwind_init() if -kk or --stack-traces=source is
given.
(print_stopped): Use the new variable
name, stack_trace_mode.
* src/syscall.c (update_personality): Use the new variable
name, stack_trace_mode.
(syscall_entering_trace): Ditto.
(syscall_exiting_trace): Ditto.
* src/unwind.h (unwind_call_action_fn): add parameters
source_filename and source_line as parameters.
(struct unwind_unwinder_t::init): Add a bool parameter.
* src/unwind.c (unwind_init): Add with_srcinfo parameter and
pass it to init method of backend.
(STACK_ENTRY_SYMBOL_WITH_SRCINFO_FMT): New format string for
printing stack trace entries with source code information.
(print_call_cb): Add source_filename and source_line as
parameters. Build stack trace entries with
STACK_ENTRY_SYMBOL_WITH_SRCINFO_FMT if source_filename is given.
(queue_put): Add source_filename and source_line as parameters.
Pass them to sprint_call_or_error().
(queue_put_call): Do the same as queue_put.
(queue_put_error): Pass NULL as source_filename, 0 as source_line to
queue_put().
* src/unwind-libdw.c (struct cache_entry::source_filename,
struct cache_entry::source_line): New members.
(with_srcinfo): New file local variable.
(init): Set with_srcinfo.
(frame_callback): Get source information for pc from
dwfl_module_getsrc() and dwfl_lineinfo().  Pass the source information
to call_action method and fill the source_filename and source_line members
of cache entry.
* src/unwind-libunwind.c (init, print_stack_frame): Adjust the code to
the updated interface declared in unwind.h.
* tests/Makefile.am (check_SCRIPTS): add strace-kk.test.
(check_DATA): strace-kk.expected.
[ENABLE_STACKTRACE]/[USE_LIBDW] (STACKTRACE_TESTS): add
strace-kk.test.
* tests/strace-k.test: Add KOPT_SHORT and KOPT_LONG variables
to make the test case reusable for testing -kk option. Don't
hardcode -k and --stack-traces. Add a new pattern for
testing -kk/--stack-traces=source.
* tests/strace-kk.expected: New file.
* tests/strace-kk.test: New test case.
* tests/options-syntax.test: update expecting output.

Changes in v2:
* fold too long lines,
* withdraw the changes of diagnostics about combining -kk and -c,
* update the error messages in src/strace.c (init::k::STACK_TRACE_ON,
(init::k::GETOPT_STACK),
* use "source" as a specifier to enable "stack traces with source
  information" instead of "+source",
* fix a typo about the short option (s/K/k/) in the man page, add
* add comments about description about -k/-kk option.

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
---
 NEWS                      |  2 ++
 doc/strace.1.in           | 15 ++++++--
 src/defs.h                | 11 ++++--
 src/filter_seccomp.c      |  2 +-
 src/strace.c              | 72 +++++++++++++++++++++++++++++++++------
 src/syscall.c             |  6 ++--
 src/unwind-libdw.c        | 26 ++++++++++++--
 src/unwind-libunwind.c    |  5 +--
 src/unwind.c              | 49 +++++++++++++++++++++-----
 src/unwind.h              | 10 ++++--
 tests/Makefile.am         |  5 +++
 tests/options-syntax.test |  2 ++
 tests/strace-k.test       | 38 ++++++++++++++++++---
 tests/strace-kk.expected  |  2 ++
 tests/strace-kk.test      | 13 +++++++
 15 files changed, 219 insertions(+), 39 deletions(-)
 create mode 100644 tests/strace-kk.expected
 create mode 100755 tests/strace-kk.test

diff --git a/NEWS b/NEWS
index a93bf7105..ddedc3bb1 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,8 @@ Noteworthy changes in release ?.? (????-??-??)
 ==============================================
 
 * Improvements
+  * Implemented -kk/--stack-traces=source option.
+    The option requires libdw backend.
 
 Noteworthy changes in release 6.6 (2023-10-31)
 ==============================================
diff --git a/doc/strace.1.in b/doc/strace.1.in
index 1f4d5f375..f61afc982 100644
--- a/doc/strace.1.in
+++ b/doc/strace.1.in
@@ -38,7 +38,11 @@
 strace \- trace system calls and signals
 .SH SYNOPSIS
 .SY strace
-.if '@ENABLE_STACKTRACE_FALSE@'#' .OP \-ACdffhikqqrtttTvVwxxyyYzZ
+.\" -kk option is available:
+.if '@ENABLE_STACKTRACE_FALSE@'#' .if '@USE_LIBDW_FALSE@'#' .OP \-ACdffhikkqqrtttTvVwxxyyYzZ
+.\" only -k option is available:
+.if '@ENABLE_STACKTRACE_FALSE@'#' .if '@USE_LIBUNWIND_FALSE@'#' .OP \-ACdffhikqqrtttTvVwxxyyYzZ
+.\" no -k option is available:
 .if '@ENABLE_STACKTRACE_TRUE@'#' .OP \-ACdffhiqqrtttTvVwxxyyYzZ
 .OP \-a column
 .OP \-b execve
@@ -996,9 +1000,16 @@ Print the syscall number.
 .if '@ENABLE_STACKTRACE_FALSE@'#' .TP
 .if '@ENABLE_STACKTRACE_FALSE@'#' .B \-k
 .if '@ENABLE_STACKTRACE_FALSE@'#' .TQ
-.if '@ENABLE_STACKTRACE_FALSE@'#' .B \-\-stack\-traces
+.if '@ENABLE_STACKTRACE_FALSE@'#' .B \-\-stack\-traces [= symbol ]
 .if '@ENABLE_STACKTRACE_FALSE@'#' Print the execution stack trace of the traced
 .if '@ENABLE_STACKTRACE_FALSE@'#' processes after each system call.
+.if '@USE_LIBDW_FALSE@'#' .TP
+.if '@USE_LIBDW_FALSE@'#' .B \-kk
+.if '@USE_LIBDW_FALSE@'#' .TQ
+.if '@USE_LIBDW_FALSE@'#' .B \-\-stack\-traces [= source ]
+.if '@USE_LIBDW_FALSE@'#' Print the execution stack trace and source code inforamation of the traced
+.if '@USE_LIBDW_FALSE@'#' processes after each system call. This option expects the target program is compiled
+.if '@USE_LIBDW_FALSE@'#' with appropriate debug options: "\-g" (gcc), or "\-g \-gdwarf-aranges" (clang).
 .TP
 .BI "\-o " filename
 .TQ
diff --git a/src/defs.h b/src/defs.h
index 6632a21d8..6f1f25680 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -544,11 +544,16 @@ enum xflag_opts {
 extern unsigned xflag;
 extern bool followfork;
 extern bool output_separately;
+enum stack_trace_modes {
+	STACK_TRACE_OFF,
+	STACK_TRACE_ON,
+	STACK_TRACE_WITH_SRCINFO,
+};
 # ifdef ENABLE_STACKTRACE
 /* if this is true do the stack trace for every system call */
-extern bool stack_trace_enabled;
+extern enum stack_trace_modes stack_trace_mode;
 # else
-#  define stack_trace_enabled 0
+#  define stack_trace_mode STACK_TRACE_OFF
 # endif
 extern unsigned max_strlen;
 extern unsigned os_release;
@@ -1560,7 +1565,7 @@ extern void print_ticks_d(int64_t val, long freq, unsigned int precision);
 extern void print_clock_t(uint64_t val);
 
 # ifdef ENABLE_STACKTRACE
-extern void unwind_init(void);
+extern void unwind_init(bool);
 extern void unwind_tcb_init(struct tcb *);
 extern void unwind_tcb_fin(struct tcb *);
 extern void unwind_tcb_print(struct tcb *);
diff --git a/src/filter_seccomp.c b/src/filter_seccomp.c
index 219adc33c..4a2be745a 100644
--- a/src/filter_seccomp.c
+++ b/src/filter_seccomp.c
@@ -257,7 +257,7 @@ traced_by_seccomp(unsigned int scno, unsigned int p)
 {
 	unsigned int always_trace_flags =
 		TRACE_INDIRECT_SUBCALL | TRACE_SECCOMP_DEFAULT |
-		(stack_trace_enabled ? MEMORY_MAPPING_CHANGE : 0) |
+		(stack_trace_mode ? MEMORY_MAPPING_CHANGE : 0) |
 		(is_number_in_set(DECODE_PID_COMM, decode_pid_set) ?
 		 COMM_CHANGE : 0);
 	return sysent_vec[p][scno].sys_flags & always_trace_flags ||
diff --git a/src/strace.c b/src/strace.c
index 9aa0eda11..f63adc043 100644
--- a/src/strace.c
+++ b/src/strace.c
@@ -51,7 +51,7 @@ extern char *optarg;
 
 #ifdef ENABLE_STACKTRACE
 /* if this is true do the stack trace for every system call */
-bool stack_trace_enabled;
+enum stack_trace_modes stack_trace_mode;
 #endif
 
 #define my_tkill(tid, sig) syscall(__NR_tkill, (tid), (sig))
@@ -270,7 +270,11 @@ static void
 usage(void)
 {
 #ifdef ENABLE_STACKTRACE
+# ifdef USE_LIBDW
+# define K_OPT "kk"
+# else
 # define K_OPT "k"
+# endif
 #else
 # define K_OPT ""
 #endif
@@ -394,9 +398,15 @@ Output format:\n\
 "
 #ifdef ENABLE_STACKTRACE
 "\
-  -k, --stack-traces\n\
+  -k, --stack-traces[=symbol]\n\
                  obtain stack trace between each syscall\n\
 "
+#ifdef USE_LIBDW
+"\
+  -kk, --stack-traces=source\n\
+                 obtain stack trace and source info between each syscall\n\
+"
+#endif
 #endif
 "\
   -n, --syscall-number\n\
@@ -944,7 +954,7 @@ after_successful_attach(struct tcb *tcp, const unsigned int flags)
 	}
 
 #ifdef ENABLE_STACKTRACE
-	if (stack_trace_enabled)
+	if (stack_trace_mode)
 		unwind_tcb_init(tcp);
 #endif
 }
@@ -1096,7 +1106,7 @@ droptcb(struct tcb *tcp)
 	free_tcb_priv_data(tcp);
 
 #ifdef ENABLE_STACKTRACE
-	if (stack_trace_enabled)
+	if (stack_trace_mode)
 		unwind_tcb_fin(tcp);
 #endif
 
@@ -2276,6 +2286,7 @@ init(int argc, char *argv[])
 		GETOPT_OUTPUT_SEPARATELY,
 		GETOPT_PIDNS_TRANSLATION,
 		GETOPT_SYSCALL_LIMIT,
+		GETOPT_STACK,
 		GETOPT_TS,
 		GETOPT_TIPS,
 		GETOPT_ARGV0,
@@ -2315,7 +2326,7 @@ init(int argc, char *argv[])
 		{ "instruction-pointer", no_argument,      0, 'i' },
 		{ "interruptible",	required_argument, 0, 'I' },
 		{ "kill-on-exit",	no_argument,	   0, GETOPT_KILL_ON_EXIT },
-		{ "stack-traces",	no_argument,	   0, 'k' },
+		{ "stack-traces" ,	optional_argument, 0, GETOPT_STACK },
 		{ "syscall-limit",	required_argument, 0, GETOPT_SYSCALL_LIMIT },
 		{ "syscall-number",	no_argument,	   0, 'n' },
 		{ "output",		required_argument, 0, 'o' },
@@ -2453,12 +2464,51 @@ init(int argc, char *argv[])
 			break;
 		case 'k':
 #ifdef ENABLE_STACKTRACE
-			stack_trace_enabled = true;
+			switch (stack_trace_mode) {
+			case STACK_TRACE_OFF:
+				stack_trace_mode = STACK_TRACE_ON;
+				break;
+			case STACK_TRACE_ON:
+# ifdef USE_LIBDW
+				stack_trace_mode = STACK_TRACE_WITH_SRCINFO;
+# else
+				error_msg_and_die("Stack traces with "
+						  "source line information (-kk/"
+						  "--stack-traces=source option) "
+						  "are not supported by this "
+						  "build of strace");
+# endif	/* USE_LIBDW */
+				break;
+			default:
+				error_msg_and_die("Too many -k options");
+			}
+#else
+			error_msg_and_die("Stack traces (-k/--stack-traces "
+					  "option) are not supported by this "
+					  "build of strace");
+#endif /* ENABLE_STACKTRACE */
+			break;
+		case GETOPT_STACK:
+#ifdef ENABLE_STACKTRACE
+			if (optarg == NULL || strcmp(optarg, "symbol") == 0)
+				stack_trace_mode = STACK_TRACE_ON;
+			else if (strcmp(optarg, "source") == 0) {
+# ifdef USE_LIBDW
+				stack_trace_mode = STACK_TRACE_WITH_SRCINFO;
+# else
+				error_msg_and_die("Stack traces with "
+						  "source line information "
+						  "(-kk/--stack-traces=source option) "
+						  "are not supported by this "
+						  "build of strace");
+# endif /* USE_LIBDW */
+			} else
+				error_opt_arg(c, lopt, optarg);
 #else
 			error_msg_and_die("Stack traces (-k/--stack-traces "
 					  "option) are not supported by this "
 					  "build of strace");
-#endif
+#endif /* ENABLE_STACKTRACE */
 			break;
 		case GETOPT_KILL_ON_EXIT:
 			opt_kill_on_exit = true;
@@ -2791,7 +2841,7 @@ init(int argc, char *argv[])
 		if (iflag)
 			error_msg("-i/--instruction-pointer has no effect "
 				  "with -c/--summary-only");
-		if (stack_trace_enabled)
+		if (stack_trace_mode)
 			error_msg("-k/--stack-traces has no effect "
 				  "with -c/--summary-only");
 		if (nflag)
@@ -2847,8 +2897,8 @@ init(int argc, char *argv[])
 	set_sighandler(SIGCHLD, SIG_DFL, &params_for_tracee.child_sa);
 
 #ifdef ENABLE_STACKTRACE
-	if (stack_trace_enabled)
-		unwind_init();
+	if (stack_trace_mode)
+		unwind_init(stack_trace_mode == STACK_TRACE_WITH_SRCINFO);
 #endif
 
 	/* See if they want to run as another user. */
@@ -3302,7 +3352,7 @@ print_stopped(struct tcb *tcp, const siginfo_t *si, const unsigned int sig)
 		line_ended();
 
 #ifdef ENABLE_STACKTRACE
-		if (stack_trace_enabled)
+		if (stack_trace_mode)
 			unwind_tcb_print(tcp);
 #endif
 	}
diff --git a/src/syscall.c b/src/syscall.c
index def95476f..6d9e843fe 100644
--- a/src/syscall.c
+++ b/src/syscall.c
@@ -268,7 +268,7 @@ update_personality(struct tcb *tcp, unsigned int personality)
 	}
 
 # if defined(ENABLE_STACKTRACE) && !defined(USE_LIBUNWIND)
-	if (stack_trace_enabled) {
+	if (stack_trace_mode) {
 		unwind_tcb_fin(tcp);
 		unwind_tcb_init(tcp);
 	}
@@ -662,7 +662,7 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
 	}
 
 #ifdef ENABLE_STACKTRACE
-	if (stack_trace_enabled &&
+	if (stack_trace_mode &&
 	    !check_exec_syscall(tcp) &&
 	    tcp_sysent(tcp)->sys_flags & STACKTRACE_CAPTURE_ON_ENTER) {
 		unwind_tcb_capture(tcp);
@@ -1026,7 +1026,7 @@ syscall_exiting_trace(struct tcb *tcp, struct timespec *ts, int res)
 	line_ended();
 
 #ifdef ENABLE_STACKTRACE
-	if (stack_trace_enabled)
+	if (stack_trace_mode)
 		unwind_tcb_print(tcp);
 #endif
 	return 0;
diff --git a/src/unwind-libdw.c b/src/unwind-libdw.c
index 8befa6bd9..211982a98 100644
--- a/src/unwind-libdw.c
+++ b/src/unwind-libdw.c
@@ -36,6 +36,9 @@ struct cache_entry {
 	GElf_Off off;
 	Dwarf_Addr true_offset;
 
+	const char *source_filename;
+	int source_line;
+
 	/* replacement */
 	unsigned long long last_use;
 };
@@ -48,6 +51,7 @@ struct ctx {
 
 static unsigned long long mapping_generation = 1;
 static unsigned long long uwcache_clock;
+static bool with_srcinfo;
 
 static void
 update_mapping_generation(struct tcb *tcp, void *unused)
@@ -56,8 +60,9 @@ update_mapping_generation(struct tcb *tcp, void *unused)
 }
 
 static void
-init(void)
+init(bool with_srcinfo_)
 {
+	with_srcinfo = with_srcinfo_;
 	mmap_notify_register_client(update_mapping_generation, NULL);
 }
 
@@ -184,7 +189,8 @@ frame_callback(Dwfl_Frame *state, void *arg)
 	if (find_bucket(user_data->ctx, pc, &ce)) {
 		user_data->call_action(user_data->data,
 				       ce->modname, ce->symname,
-			               ce->off, ce->true_offset);
+			               ce->off, ce->true_offset,
+				       ce->source_filename, ce->source_line);
 	} else {
 		Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(state));
 		Dwfl_Module *mod = dwfl_addrmodule(dwfl, pc);
@@ -195,14 +201,26 @@ frame_callback(Dwfl_Frame *state, void *arg)
 			const char *symname = NULL;
 			GElf_Sym sym;
 			Dwarf_Addr true_offset = pc;
+			const char *source_filename = NULL;
+			int source_line = 0;
 
 			modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL,
 						   NULL, NULL, NULL);
 			symname = dwfl_module_addrinfo(mod, pc, &off, &sym,
 						       NULL, NULL, NULL);
 			dwfl_module_relocate_address(mod, &true_offset);
+			if (with_srcinfo) {
+				Dwfl_Line *dwfl_line;
+
+				dwfl_line = dwfl_module_getsrc(mod, pc);
+				if (dwfl_line)
+					source_filename = dwfl_lineinfo(dwfl_line, NULL,
+									&source_line, NULL,
+									NULL, NULL);
+			}
 			user_data->call_action(user_data->data, modname, symname,
-					       off, true_offset);
+					       off, true_offset,
+					       source_filename, source_line);
 
 			ce->generation = mapping_generation;
 			ce->pc = pc;
@@ -210,6 +228,8 @@ frame_callback(Dwfl_Frame *state, void *arg)
 			ce->symname = symname;
 			ce->off = off;
 			ce->true_offset = true_offset;
+			ce->source_filename = source_filename;
+			ce->source_line = source_line;
 			ce->last_use = uwcache_clock++;
 		}
 	}
diff --git a/src/unwind-libunwind.c b/src/unwind-libunwind.c
index 9da084307..ce0ca66fa 100644
--- a/src/unwind-libunwind.c
+++ b/src/unwind-libunwind.c
@@ -14,7 +14,7 @@
 static unw_addr_space_t libunwind_as;
 
 static void
-init(void)
+init(bool unused_with_srcinfo)
 {
 	mmap_cache_enable();
 
@@ -90,7 +90,8 @@ print_stack_frame(struct tcb *tcp,
 			    entry->binary_filename,
 			    *symbol_name,
 			    function_offset,
-			    true_offset);
+			    true_offset,
+			    NULL, 0);
 
 		return 0;
 	}
diff --git a/src/unwind.c b/src/unwind.c
index 7f00b5fb5..8f5528628 100644
--- a/src/unwind.c
+++ b/src/unwind.c
@@ -37,10 +37,10 @@ static void queue_print(struct unwind_queue_t *queue);
 static const char asprintf_error_str[] = "???";
 
 void
-unwind_init(void)
+unwind_init(bool with_srcinfo)
 {
 	if (unwinder.init)
-		unwinder.init();
+		unwinder.init(with_srcinfo);
 }
 
 void
@@ -82,6 +82,14 @@ unwind_tcb_fin(struct tcb *tcp)
  * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
  * ./a.out() [0x400569]
  */
+#define STACK_ENTRY_SYMBOL_WITH_SRCINFO_FMT(SYM)\
+	" > %s(%s+0x%lx) [0x%lx] %s:%d\n",	\
+	binary_filename,			\
+	(SYM),					\
+	(unsigned long) function_offset,	\
+	true_offset,				\
+	source_filename,			\
+	source_line
 #define STACK_ENTRY_SYMBOL_FMT(SYM)		\
 	" > %s(%s+0x%lx) [0x%lx]\n",		\
 	binary_filename,			\
@@ -103,7 +111,9 @@ print_call_cb(void *dummy,
 	      const char *binary_filename,
 	      const char *symbol_name,
 	      unwind_function_offset_t function_offset,
-	      unsigned long true_offset)
+	      unsigned long true_offset,
+	      const char *source_filename,
+	      int source_line)
 {
 	if (symbol_name && (symbol_name[0] != '\0')) {
 #ifdef USE_DEMANGLE
@@ -111,7 +121,13 @@ print_call_cb(void *dummy,
 			cplus_demangle(symbol_name,
 				       DMGL_AUTO | DMGL_PARAMS);
 #endif
-		tprintf_string(STACK_ENTRY_SYMBOL_FMT(
+		source_filename
+			? tprintf_string(STACK_ENTRY_SYMBOL_WITH_SRCINFO_FMT(
+#ifdef USE_DEMANGLE
+						      demangled_name ? demangled_name :
+#endif
+						      symbol_name))
+			: tprintf_string(STACK_ENTRY_SYMBOL_FMT(
 #ifdef USE_DEMANGLE
 						      demangled_name ? demangled_name :
 #endif
@@ -146,6 +162,8 @@ sprint_call_or_error(const char *binary_filename,
 		     const char *symbol_name,
 		     unwind_function_offset_t function_offset,
 		     unsigned long true_offset,
+		     const char *source_filename,
+		     int source_line,
 		     const char *error)
 {
 	char *output_line = NULL;
@@ -157,8 +175,15 @@ sprint_call_or_error(const char *binary_filename,
 			cplus_demangle(symbol_name,
 				       DMGL_AUTO | DMGL_PARAMS);
 #endif
-		n = asprintf(&output_line,
-			     STACK_ENTRY_SYMBOL_FMT(
+		n = source_filename
+			? asprintf(&output_line,
+				   STACK_ENTRY_SYMBOL_WITH_SRCINFO_FMT(
+#ifdef USE_DEMANGLE
+						    demangled_name ? demangled_name :
+#endif
+						    symbol_name))
+			: asprintf(&output_line,
+				   STACK_ENTRY_SYMBOL_FMT(
 #ifdef USE_DEMANGLE
 						    demangled_name ? demangled_name :
 #endif
@@ -193,6 +218,8 @@ queue_put(struct unwind_queue_t *queue,
 	  const char *symbol_name,
 	  unwind_function_offset_t function_offset,
 	  unsigned long true_offset,
+	  const char *source_filename,
+	  int source_line,
 	  const char *error)
 {
 	struct call_t *call;
@@ -202,6 +229,8 @@ queue_put(struct unwind_queue_t *queue,
 						 symbol_name,
 						 function_offset,
 						 true_offset,
+						 source_filename,
+						 source_line,
 						 error);
 	call->next = NULL;
 
@@ -219,13 +248,17 @@ queue_put_call(void *queue,
 	       const char *binary_filename,
 	       const char *symbol_name,
 	       unwind_function_offset_t function_offset,
-	       unsigned long true_offset)
+	       unsigned long true_offset,
+	       const char *source_filename,
+	       int source_line)
 {
 	queue_put(queue,
 		  binary_filename,
 		  symbol_name,
 		  function_offset,
 		  true_offset,
+		  source_filename,
+		  source_line,
 		  NULL);
 }
 
@@ -234,7 +267,7 @@ queue_put_error(void *queue,
 		const char *error,
 		unsigned long ip)
 {
-	queue_put(queue, NULL, NULL, 0, ip, error);
+	queue_put(queue, NULL, NULL, 0, ip, NULL, 0, error);
 }
 
 static void
diff --git a/src/unwind.h b/src/unwind.h
index d36b3282d..952917c10 100644
--- a/src/unwind.h
+++ b/src/unwind.h
@@ -23,7 +23,13 @@ typedef void (*unwind_call_action_fn)(void *data,
 				      const char *binary_filename,
 				      const char *symbol_name,
 				      unwind_function_offset_t function_offset,
-				      unsigned long true_offset);
+				      unsigned long true_offset,
+				      /* source information:
+				       * source_filename can be NULL.
+				       * In that case, source_line is never referenced. */
+				      const char *source_filename,
+				      int source_line
+				      );
 typedef void (*unwind_error_action_fn)(void *data,
 				       const char *error,
 				       unsigned long true_offset);
@@ -32,7 +38,7 @@ struct unwind_unwinder_t {
 	const char *name;
 
 	/* Initialize the unwinder. */
-	void   (*init)(void);
+	void   (*init)(bool);
 
 	/* Make/destroy the context data attached to tcb. */
 	void * (*tcb_init)(struct tcb *);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d74674b20..5bec13c91 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -490,6 +490,9 @@ include gen_tests.am
 
 if ENABLE_STACKTRACE
 STACKTRACE_TESTS = strace-k.test strace-k-p.test
+if USE_LIBDW
+STACKTRACE_TESTS += strace-kk.test
+endif
 if USE_DEMANGLE
 STACKTRACE_TESTS += strace-k-demangle.test
 endif
@@ -693,6 +696,7 @@ check_SCRIPTS = \
 	strace-k-demangle.test \
 	strace-k-p.test \
 	strace-k.test \
+	strace-kk.test \
 	syntax.sh \
 	# end of check_SCRIPTS
 
@@ -749,6 +753,7 @@ check_DATA = \
 	strace-k-demangle.expected \
 	strace-k-p.expected \
 	strace-k.expected \
+	strace-kk.expected \
 	strace-r.expected \
 	strace.supp \
 	strauss_body.exp \
diff --git a/tests/options-syntax.test b/tests/options-syntax.test
index da18662b9..f437cafba 100755
--- a/tests/options-syntax.test
+++ b/tests/options-syntax.test
@@ -418,6 +418,8 @@ check_e_using_grep 'ptrace_setoptions = 0x[[:xdigit:]]+' --debug /
 if [ -z "$compiled_with_stacktrace" ]; then
 	check_e "Stack traces (-k/--stack-traces option) are not supported by this build of strace" -k
 	check_e "Stack traces (-k/--stack-traces option) are not supported by this build of strace" --stack-traces
+	check_e "Stack traces (-k/--stack-traces option) are not supported by this build of strace" --stack-traces=symbol
+	check_e "Stack traces (-k/--stack-traces option) are not supported by this build of strace" --stack-traces=source
 fi
 
 args='-p 2147483647'
diff --git a/tests/strace-k.test b/tests/strace-k.test
index b303df6b4..d8dd68041 100755
--- a/tests/strace-k.test
+++ b/tests/strace-k.test
@@ -12,6 +12,8 @@
 . "${srcdir=.}/init.sh"
 
 : "${ATTACH_MODE=0}"
+: "${KOPT_SHORT=-k}"
+: "${KOPT_LONG=--stack-traces}"
 
 # strace -k is implemented using /proc/$pid/maps
 [ -f /proc/self/maps ] ||
@@ -43,13 +45,13 @@ if [ "x${ATTACH_MODE}" = "x1" ]; then
 			fail_ 'set_ptracer_any failed'
 	done
 
-	run_strace --trace=chdir --stack-trace --attach="$tracee_pid"
+	run_strace --trace=chdir ${KOPT_LONG} --attach="$tracee_pid"
 else
-	run_strace -e chdir -k $args
+	run_strace -e chdir ${KOPT_SHORT} $args
 fi
 
 expected="$srcdir/$NAME.expected"
-awk '
+awk_script_common='
 /^[^ ]/ {
 	if (out != "")
 		print out
@@ -68,12 +70,40 @@ awk '
 	}
 }
 
+'
+
+awk_script_symbol='
 /^ >[^(]+\(([^+]+)\+0x[a-f0-9]+\) / && !stop {
 	sym = gensub(/^ >[^(]+\(([^+]+)\+0x[a-f0-9]+\) .*$/, "\\1", 1)
 	out = out " " sym
 	if (sym == "main")
 		stop = 1
-}' "$LOG" > "$OUT"
+}
+
+'
+
+awk_script_source='
+/^ >[^(]+\(([^+]+)\+0x[a-f0-9]+\) \[0x[a-f0-9]+\] ([^:]+):([0-9]+)$/ && !stop {
+	sym = gensub(/^ >[^(]+\(([^+]+)\+0x[a-f0-9]+\) .*$/, "\\1", 1)
+	if (sym == "main" || sym ~ /f[0-9]/) {
+		file = gensub(/^ >[^(]+\(([^+]+)\+0x[a-f0-9]+\) \[0x[a-f0-9]+\] ([^:]+):([0-9]+)$/, "\\2", 1)
+		line = gensub(/^ >[^(]+\(([^+]+)\+0x[a-f0-9]+\) \[0x[a-f0-9]+\] ([^:]+):([0-9]+)$/, "\\3", 1)
+		sub(".*/", "", file)
+		out = out " " sym "<" file ":" line ">"
+		if (sym == "main")
+			stop = 1
+	}
+}
+
+'
+
+if [ "${KOPT_LONG}" = "--stack-traces" ]; then
+    awk_script="${awk_script_common}${awk_script_symbol}"
+else
+    awk_script="${awk_script_common}${awk_script_source}"
+fi
+
+awk "${awk_script}" "$LOG" > "$OUT"
 
 LC_ALL=C grep -E -x -f "$expected" < "$OUT" > /dev/null || {
 	cat >&2 <<__EOF__
diff --git a/tests/strace-kk.expected b/tests/strace-kk.expected
new file mode 100644
index 000000000..f070db89f
--- /dev/null
+++ b/tests/strace-kk.expected
@@ -0,0 +1,2 @@
+chdir f3<stack-fcall-3.c:18> f2<stack-fcall-2.c:15> f1<stack-fcall-1.c:15> f0<stack-fcall-0.c:15> main<stack-fcall.c:25>
+SIGURG f3<stack-fcall-3.c:21> f2<stack-fcall-2.c:15> f1<stack-fcall-1.c:15> f0<stack-fcall-0.c:15> main<stack-fcall.c:25>
diff --git a/tests/strace-kk.test b/tests/strace-kk.test
new file mode 100755
index 000000000..032a34992
--- /dev/null
+++ b/tests/strace-kk.test
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# Ensure that strace -kk works.
+#
+# Copyright (c) 2023 The strace developers.
+# All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+KOPT_SHORT=-kk
+KOPT_LONG=--stack-traces=source
+
+. "${srcdir=.}"/strace-k.test
-- 
2.41.0



More information about the Strace-devel mailing list