Home | History | Annotate | Download | only in strace
      1 /*
      2  * This file is based on a patch submitted by Mark Wielaard <mjw (at) redhat.com>
      3  * to ltrace project:
      4  * https://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=dfefa9f057857735a073ea655f5cb34351032c8e
      5  *
      6  * It was re-licensed for strace by the original author:
      7  * https://lists.strace.io/pipermail/strace-devel/2018-March/008063.html
      8  *
      9  * Copyright (c) 2014-2018 Mark Wielaard <mjw (at) redhat.com>
     10  * Copyright (c) 2018 Masatake YAMATO <yamato (at) redhat.com>
     11  * Copyright (c) 2018 The strace developers.
     12  * All rights reserved.
     13  *
     14  * Redistribution and use in source and binary forms, with or without
     15  * modification, are permitted provided that the following conditions
     16  * are met:
     17  * 1. Redistributions of source code must retain the above copyright
     18  *    notice, this list of conditions and the following disclaimer.
     19  * 2. Redistributions in binary form must reproduce the above copyright
     20  *    notice, this list of conditions and the following disclaimer in the
     21  *    documentation and/or other materials provided with the distribution.
     22  * 3. The name of the author may not be used to endorse or promote products
     23  *    derived from this software without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     26  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     27  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     28  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     30  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     34  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     35  */
     36 
     37 #include "defs.h"
     38 #include "unwind.h"
     39 #include "mmap_notify.h"
     40 #include <elfutils/libdwfl.h>
     41 
     42 struct ctx {
     43 	Dwfl *dwfl;
     44 	unsigned int last_proc_updating;
     45 };
     46 
     47 static unsigned int mapping_generation;
     48 
     49 static void
     50 update_mapping_generation(struct tcb *tcp, void *unused)
     51 {
     52 	mapping_generation++;
     53 }
     54 
     55 static void
     56 init(void)
     57 {
     58 	mmap_notify_register_client(update_mapping_generation, NULL);
     59 }
     60 
     61 static void *
     62 tcb_init(struct tcb *tcp)
     63 {
     64 	static const Dwfl_Callbacks proc_callbacks = {
     65 		.find_elf = dwfl_linux_proc_find_elf,
     66 		.find_debuginfo = dwfl_standard_find_debuginfo
     67 	};
     68 
     69 	Dwfl *dwfl = dwfl_begin(&proc_callbacks);
     70 	if (dwfl == NULL) {
     71 		error_msg("dwfl_begin: %s", dwfl_errmsg(-1));
     72 		return NULL;
     73 	}
     74 
     75 	int r = dwfl_linux_proc_attach(dwfl, tcp->pid, true);
     76 	if (r) {
     77 		const char *msg = NULL;
     78 
     79 		if (r < 0)
     80 			msg = dwfl_errmsg(-1);
     81 		else if (r > 0)
     82 			msg = strerror(r);
     83 
     84 		error_msg("dwfl_linux_proc_attach returned an error"
     85 			  " for process %d: %s", tcp->pid, msg);
     86 		dwfl_end(dwfl);
     87 		return NULL;
     88 	}
     89 
     90 	struct ctx *ctx = xmalloc(sizeof(*ctx));
     91 	ctx->dwfl = dwfl;
     92 	ctx->last_proc_updating = 0;
     93 	return ctx;
     94 }
     95 
     96 static void
     97 tcb_fin(struct tcb *tcp)
     98 {
     99 	struct ctx *ctx = tcp->unwind_ctx;
    100 	if (ctx) {
    101 		dwfl_end(ctx->dwfl);
    102 		free(ctx);
    103 	}
    104 }
    105 
    106 static void
    107 flush_cache_maybe(struct tcb *tcp)
    108 {
    109 	struct ctx *ctx = tcp->unwind_ctx;
    110 	if (!ctx)
    111 		return;
    112 
    113 	if (ctx->last_proc_updating == mapping_generation)
    114 		return;
    115 
    116 	int r = dwfl_linux_proc_report(ctx->dwfl, tcp->pid);
    117 
    118 	if (r < 0)
    119 		error_msg("dwfl_linux_proc_report returned an error"
    120 			  " for pid %d: %s", tcp->pid, dwfl_errmsg(-1));
    121 	else if (r > 0)
    122 		error_msg("dwfl_linux_proc_report returned an error"
    123 			  " for pid %d", tcp->pid);
    124 	else if (dwfl_report_end(ctx->dwfl, NULL, NULL) != 0)
    125 		error_msg("dwfl_report_end returned an error"
    126 			  " for pid %d: %s", tcp->pid, dwfl_errmsg(-1));
    127 
    128 	ctx->last_proc_updating = mapping_generation;
    129 }
    130 
    131 struct frame_user_data {
    132 	unwind_call_action_fn call_action;
    133 	unwind_error_action_fn error_action;
    134 	void *data;
    135 	int stack_depth;
    136 };
    137 
    138 static int
    139 frame_callback(Dwfl_Frame *state, void *arg)
    140 {
    141 	struct frame_user_data *user_data = arg;
    142 	Dwarf_Addr pc;
    143 	bool isactivation;
    144 
    145 	if (!dwfl_frame_pc(state, &pc, &isactivation)) {
    146 		/* Propagate the error to the caller.  */
    147 		return -1;
    148 	}
    149 
    150 	if (!isactivation)
    151 		pc--;
    152 
    153 	Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(state));
    154 	Dwfl_Module *mod = dwfl_addrmodule(dwfl, pc);
    155 	GElf_Off off = 0;
    156 
    157 	if (mod != NULL) {
    158 		const char *modname = NULL;
    159 		const char *symname = NULL;
    160 		GElf_Sym sym;
    161 		Dwarf_Addr true_offset = pc;
    162 
    163 		modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL,
    164 					   NULL, NULL, NULL);
    165 		symname = dwfl_module_addrinfo(mod, pc, &off, &sym,
    166 					       NULL, NULL, NULL);
    167 		dwfl_module_relocate_address(mod, &true_offset);
    168 		user_data->call_action(user_data->data, modname, symname,
    169 				       off, true_offset);
    170 	}
    171 	/* Max number of frames to print reached? */
    172 	if (user_data->stack_depth-- == 0)
    173 		return DWARF_CB_ABORT;
    174 
    175 	return DWARF_CB_OK;
    176 }
    177 
    178 static void
    179 tcb_walk(struct tcb *tcp,
    180 	 unwind_call_action_fn call_action,
    181 	 unwind_error_action_fn error_action,
    182 	 void *data)
    183 {
    184 	struct ctx *ctx = tcp->unwind_ctx;
    185 	if (!ctx)
    186 		return;
    187 
    188 	struct frame_user_data user_data = {
    189 		.call_action = call_action,
    190 		.error_action = error_action,
    191 		.data = data,
    192 		.stack_depth = 256,
    193 	};
    194 
    195 	flush_cache_maybe(tcp);
    196 
    197 	int r = dwfl_getthread_frames(ctx->dwfl, tcp->pid, frame_callback,
    198 				      &user_data);
    199 	if (r)
    200 		error_action(data,
    201 			     r < 0 ? dwfl_errmsg(-1) : "too many stack frames",
    202 			     0);
    203 }
    204 
    205 const struct unwind_unwinder_t unwinder = {
    206 	.name = "libdw",
    207 	.init = init,
    208 	.tcb_init = tcb_init,
    209 	.tcb_fin = tcb_fin,
    210 	.tcb_walk = tcb_walk,
    211 };
    212