strace/mem.c
Masatake YAMATO f8e39d7b7a unwind: introduce queue_t for capturing stacktrace
This is the second step for splitting capturing from printing.

New `queue' field is added to tcb.  Captured stacktrace is stored here.
The field is initialized/finalized at unwind_tcb_init/unwind_tcb_fin.

New API function unwind_capture_stacktrace is added.  This function
captures the currest stack using stracktrace_walker and records it in
tcb.  It's printing is delayed to the next call of
unwind_print_stacktrace.

unwind_print_stacktrace is extended.  Now it checks queue field of
the given tcb at the start of function.  If the function finds a
captured stack trace, the latter is printed using stracktrace_walker.

Currently unwind_capture_stacktrace invocations are added directly to
handlers of mmap, munmap, mprotect, and execve.

Here is the difference of output with/without patch:

(without patch)
  execve("./test-fork", ["./test-fork"], [/* 56 vars */]) = 0
   > /usr/lib64/ld-2.18.so(check_one_fd.part.0+0x82) [0x11f0]

(with patch)
  execve("./test-fork", ["./test-fork"], [/* 54 vars */]) = 0
   > /usr/lib64/libc-2.18.so(execve+0x7) [0xbcd27]
   > /home/yamato/var/strace/strace(exec_or_die+0x10c) [0x26ac]
   > /home/yamato/var/strace/strace(startup_child+0x346) [0x134f6]
   > /home/yamato/var/strace/strace(init+0x89f) [0x13dff]
   > /home/yamato/var/strace/strace(main+0xa) [0x26ca]
   > /usr/lib64/libc-2.18.so(__libc_start_main+0xf5) [0x21d65]
   > /home/yamato/var/strace/strace(_start+0x29) [0x2799]

In older version output lines of captured elements were built when
printing.  In this version they are built when capturing the stack.
As result, unneeded dynamic memory allocations are avoided.
Suggested by Luca Clementi.

In older version the combination of snprintf and realloc were used.
In this version they are replaced with asprintf.
Suggested by Dmitry Levin.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
2014-05-30 22:55:08 +00:00

621 lines
14 KiB
C

/*
* Copyright (c) 1991, 1992 Paul Kranenburg <pk@cs.few.eur.nl>
* Copyright (c) 1993 Branko Lankester <branko@hacktic.nl>
* Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs@world.std.com>
* Copyright (c) 1996-1999 Wichert Akkerman <wichert@cistron.nl>
* Copyright (c) 2000 PocketPenguins Inc. Linux for Hitachi SuperH
* port by Greg Banks <gbanks@pocketpenguins.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "defs.h"
#include <asm/mman.h>
#include <sys/mman.h>
static unsigned long
get_pagesize()
{
static unsigned long pagesize;
if (!pagesize)
pagesize = sysconf(_SC_PAGESIZE);
return pagesize;
}
int
sys_brk(struct tcb *tcp)
{
if (entering(tcp)) {
tprintf("%#lx", tcp->u_arg[0]);
}
return RVAL_HEX;
}
#include "xlat/mmap_prot.h"
#include "xlat/mmap_flags.h"
static int
print_mmap(struct tcb *tcp, long *u_arg, unsigned long long offset)
{
if (entering(tcp)) {
#ifdef USE_LIBUNWIND
if (stack_trace_enabled) {
unwind_capture_stacktrace(tcp);
unwind_cache_invalidate(tcp);
}
#endif
/* addr */
if (!u_arg[0])
tprints("NULL, ");
else
tprintf("%#lx, ", u_arg[0]);
/* len */
tprintf("%lu, ", u_arg[1]);
/* prot */
printflags(mmap_prot, u_arg[2], "PROT_???");
tprints(", ");
/* flags */
#ifdef MAP_TYPE
printxval(mmap_flags, u_arg[3] & MAP_TYPE, "MAP_???");
addflags(mmap_flags, u_arg[3] & ~MAP_TYPE);
#else
printflags(mmap_flags, u_arg[3], "MAP_???");
#endif
tprints(", ");
/* fd */
printfd(tcp, u_arg[4]);
/* offset */
tprintf(", %#llx", offset);
}
return RVAL_HEX;
}
/* Syscall name<->function correspondence is messed up on many arches.
* For example:
* i386 has __NR_mmap == 90, and it is "old mmap", and
* also it has __NR_mmap2 == 192, which is a "new mmap with page offsets".
* But x86_64 has just one __NR_mmap == 9, a "new mmap with byte offsets".
* Confused? Me too!
*/
/* Params are pointed to by u_arg[0], offset is in bytes */
int
sys_old_mmap(struct tcb *tcp)
{
long u_arg[6];
#if defined(IA64)
/*
* IA64 processes never call this routine, they only use the
* new 'sys_mmap' interface. Only IA32 processes come here.
*/
int i;
unsigned narrow_arg[6];
if (umoven(tcp, tcp->u_arg[0], sizeof(narrow_arg), (char *) narrow_arg) == -1)
return 0;
for (i = 0; i < 6; i++)
u_arg[i] = (unsigned long) narrow_arg[i];
#elif defined(X86_64)
/* We are here only in personality 1 (i386) */
int i;
unsigned narrow_arg[6];
if (umoven(tcp, tcp->u_arg[0], sizeof(narrow_arg), (char *) narrow_arg) == -1)
return 0;
for (i = 0; i < 6; ++i)
u_arg[i] = (unsigned long) narrow_arg[i];
#else
if (umoven(tcp, tcp->u_arg[0], sizeof(u_arg), (char *) u_arg) == -1)
return 0;
#endif
return print_mmap(tcp, u_arg, (unsigned long) u_arg[5]);
}
#if defined(S390)
/* Params are pointed to by u_arg[0], offset is in pages */
int
sys_old_mmap_pgoff(struct tcb *tcp)
{
long u_arg[5];
int i;
unsigned narrow_arg[6];
unsigned long long offset;
if (umoven(tcp, tcp->u_arg[0], sizeof(narrow_arg), (char *) narrow_arg) == -1)
return 0;
for (i = 0; i < 5; i++)
u_arg[i] = (unsigned long) narrow_arg[i];
offset = narrow_arg[5];
offset *= get_pagesize();
return print_mmap(tcp, u_arg, offset);
}
#endif
/* Params are passed directly, offset is in bytes */
int
sys_mmap(struct tcb *tcp)
{
unsigned long long offset = (unsigned long) tcp->u_arg[5];
#if defined(LINUX_MIPSN32) || defined(X32)
/* Try test/x32_mmap.c */
offset = tcp->ext_arg[5];
#endif
/* Example of kernel-side handling of this variety of mmap:
* arch/x86/kernel/sys_x86_64.c::SYSCALL_DEFINE6(mmap, ...) calls
* sys_mmap_pgoff(..., off >> PAGE_SHIFT); i.e. off is in bytes,
* since the above code converts off to pages.
*/
return print_mmap(tcp, tcp->u_arg, offset);
}
/* Params are passed directly, offset is in pages */
int
sys_mmap_pgoff(struct tcb *tcp)
{
/* Try test/mmap_offset_decode.c */
unsigned long long offset;
offset = (unsigned long) tcp->u_arg[5];
offset *= get_pagesize();
return print_mmap(tcp, tcp->u_arg, offset);
}
/* Params are passed directly, offset is in 4k units */
int
sys_mmap_4koff(struct tcb *tcp)
{
unsigned long long offset;
offset = (unsigned long) tcp->u_arg[5];
offset <<= 12;
return print_mmap(tcp, tcp->u_arg, offset);
}
int
sys_munmap(struct tcb *tcp)
{
if (entering(tcp)) {
tprintf("%#lx, %lu",
tcp->u_arg[0], tcp->u_arg[1]);
#ifdef USE_LIBUNWIND
if (stack_trace_enabled)
unwind_capture_stacktrace(tcp);
#endif
}
#ifdef USE_LIBUNWIND
else {
if (stack_trace_enabled)
unwind_cache_invalidate(tcp);
}
#endif
return 0;
}
int
sys_mprotect(struct tcb *tcp)
{
if (entering(tcp)) {
tprintf("%#lx, %lu, ",
tcp->u_arg[0], tcp->u_arg[1]);
printflags(mmap_prot, tcp->u_arg[2], "PROT_???");
#ifdef USE_LIBUNWIND
if (stack_trace_enabled)
unwind_capture_stacktrace(tcp);
#endif
}
#ifdef USE_LIBUNWIND
else {
if (stack_trace_enabled)
unwind_cache_invalidate(tcp);
}
#endif
return 0;
}
#include "xlat/mremap_flags.h"
int
sys_mremap(struct tcb *tcp)
{
if (entering(tcp)) {
tprintf("%#lx, %lu, %lu, ", tcp->u_arg[0], tcp->u_arg[1],
tcp->u_arg[2]);
printflags(mremap_flags, tcp->u_arg[3], "MREMAP_???");
#ifdef MREMAP_FIXED
if ((tcp->u_arg[3] & (MREMAP_MAYMOVE | MREMAP_FIXED)) ==
(MREMAP_MAYMOVE | MREMAP_FIXED))
tprintf(", %#lx", tcp->u_arg[4]);
#endif
}
return RVAL_HEX;
}
#include "xlat/madvise_cmds.h"
int
sys_madvise(struct tcb *tcp)
{
if (entering(tcp)) {
tprintf("%#lx, %lu, ", tcp->u_arg[0], tcp->u_arg[1]);
printxval(madvise_cmds, tcp->u_arg[2], "MADV_???");
}
return 0;
}
#include "xlat/mlockall_flags.h"
int
sys_mlockall(struct tcb *tcp)
{
if (entering(tcp)) {
printflags(mlockall_flags, tcp->u_arg[0], "MCL_???");
}
return 0;
}
#ifdef MS_ASYNC
#include "xlat/mctl_sync.h"
int
sys_msync(struct tcb *tcp)
{
if (entering(tcp)) {
/* addr */
tprintf("%#lx", tcp->u_arg[0]);
/* len */
tprintf(", %lu, ", tcp->u_arg[1]);
/* flags */
printflags(mctl_sync, tcp->u_arg[2], "MS_???");
}
return 0;
}
#endif /* MS_ASYNC */
#ifdef MC_SYNC
#include "xlat/mctl_funcs.h"
#include "xlat/mctl_lockas.h"
int
sys_mctl(struct tcb *tcp)
{
int arg, function;
if (entering(tcp)) {
/* addr */
tprintf("%#lx", tcp->u_arg[0]);
/* len */
tprintf(", %lu, ", tcp->u_arg[1]);
/* function */
function = tcp->u_arg[2];
printflags(mctl_funcs, function, "MC_???");
/* arg */
arg = tcp->u_arg[3];
tprints(", ");
switch (function) {
case MC_SYNC:
printflags(mctl_sync, arg, "MS_???");
break;
case MC_LOCKAS:
printflags(mctl_lockas, arg, "MCL_???");
break;
default:
tprintf("%#x", arg);
break;
}
}
return 0;
}
#endif /* MC_SYNC */
int
sys_mincore(struct tcb *tcp)
{
if (entering(tcp)) {
tprintf("%#lx, %lu, ", tcp->u_arg[0], tcp->u_arg[1]);
} else {
unsigned long i, len;
char *vec = NULL;
len = tcp->u_arg[1];
if (syserror(tcp) || tcp->u_arg[2] == 0 ||
(vec = malloc(len)) == NULL ||
umoven(tcp, tcp->u_arg[2], len, vec) < 0)
tprintf("%#lx", tcp->u_arg[2]);
else {
tprints("[");
for (i = 0; i < len; i++) {
if (abbrev(tcp) && i >= max_strlen) {
tprints("...");
break;
}
tprints((vec[i] & 1) ? "1" : "0");
}
tprints("]");
}
free(vec);
}
return 0;
}
#if defined(ALPHA) || defined(IA64) || defined(SPARC) || defined(SPARC64)
int
sys_getpagesize(struct tcb *tcp)
{
if (exiting(tcp))
return RVAL_HEX;
return 0;
}
#endif
int
sys_remap_file_pages(struct tcb *tcp)
{
if (entering(tcp)) {
tprintf("%#lx, %lu, ", tcp->u_arg[0], tcp->u_arg[1]);
printflags(mmap_prot, tcp->u_arg[2], "PROT_???");
tprintf(", %lu, ", tcp->u_arg[3]);
#ifdef MAP_TYPE
printxval(mmap_flags, tcp->u_arg[4] & MAP_TYPE, "MAP_???");
addflags(mmap_flags, tcp->u_arg[4] & ~MAP_TYPE);
#else
printflags(mmap_flags, tcp->u_arg[4], "MAP_???");
#endif
}
return 0;
}
#define MPOL_DEFAULT 0
#define MPOL_PREFERRED 1
#define MPOL_BIND 2
#define MPOL_INTERLEAVE 3
#define MPOL_F_NODE (1<<0)
#define MPOL_F_ADDR (1<<1)
#define MPOL_MF_STRICT (1<<0)
#define MPOL_MF_MOVE (1<<1)
#define MPOL_MF_MOVE_ALL (1<<2)
#include "xlat/policies.h"
#include "xlat/mbindflags.h"
#include "xlat/mempolicyflags.h"
#include "xlat/move_pages_flags.h"
static void
get_nodes(struct tcb *tcp, unsigned long ptr, unsigned long maxnodes, int err)
{
unsigned long nlongs, size, end;
nlongs = (maxnodes + 8 * sizeof(long) - 1) / (8 * sizeof(long));
size = nlongs * sizeof(long);
end = ptr + size;
if (nlongs == 0 || ((err || verbose(tcp)) && (size * 8 == maxnodes)
&& (end > ptr))) {
unsigned long n, cur, abbrev_end;
int failed = 0;
if (abbrev(tcp)) {
abbrev_end = ptr + max_strlen * sizeof(long);
if (abbrev_end < ptr)
abbrev_end = end;
} else {
abbrev_end = end;
}
tprints(", {");
for (cur = ptr; cur < end; cur += sizeof(long)) {
if (cur > ptr)
tprints(", ");
if (cur >= abbrev_end) {
tprints("...");
break;
}
if (umoven(tcp, cur, sizeof(n), (char *) &n) < 0) {
tprints("?");
failed = 1;
break;
}
tprintf("%#0*lx", (int) sizeof(long) * 2 + 2, n);
}
tprints("}");
if (failed)
tprintf(" %#lx", ptr);
} else
tprintf(", %#lx", ptr);
tprintf(", %lu", maxnodes);
}
int
sys_mbind(struct tcb *tcp)
{
if (entering(tcp)) {
tprintf("%#lx, %lu, ", tcp->u_arg[0], tcp->u_arg[1]);
printxval(policies, tcp->u_arg[2], "MPOL_???");
get_nodes(tcp, tcp->u_arg[3], tcp->u_arg[4], 0);
tprints(", ");
printflags(mbindflags, tcp->u_arg[5], "MPOL_???");
}
return 0;
}
int
sys_set_mempolicy(struct tcb *tcp)
{
if (entering(tcp)) {
printxval(policies, tcp->u_arg[0], "MPOL_???");
get_nodes(tcp, tcp->u_arg[1], tcp->u_arg[2], 0);
}
return 0;
}
int
sys_get_mempolicy(struct tcb *tcp)
{
if (exiting(tcp)) {
int pol;
if (tcp->u_arg[0] == 0)
tprints("NULL");
else if (syserror(tcp) || umove(tcp, tcp->u_arg[0], &pol) < 0)
tprintf("%#lx", tcp->u_arg[0]);
else
printxval(policies, pol, "MPOL_???");
get_nodes(tcp, tcp->u_arg[1], tcp->u_arg[2], syserror(tcp));
tprintf(", %#lx, ", tcp->u_arg[3]);
printflags(mempolicyflags, tcp->u_arg[4], "MPOL_???");
}
return 0;
}
int
sys_migrate_pages(struct tcb *tcp)
{
if (entering(tcp)) {
tprintf("%ld, ", (long) (pid_t) tcp->u_arg[0]);
get_nodes(tcp, tcp->u_arg[2], tcp->u_arg[1], 0);
tprints(", ");
get_nodes(tcp, tcp->u_arg[3], tcp->u_arg[1], 0);
}
return 0;
}
int
sys_move_pages(struct tcb *tcp)
{
if (entering(tcp)) {
unsigned long npages = tcp->u_arg[1];
tprintf("%ld, %lu, ", tcp->u_arg[0], npages);
if (tcp->u_arg[2] == 0)
tprints("NULL, ");
else {
int i;
long puser = tcp->u_arg[2];
tprints("{");
for (i = 0; i < npages; ++i) {
void *p;
if (i > 0)
tprints(", ");
if (umove(tcp, puser, &p) < 0) {
tprints("???");
break;
}
tprintf("%p", p);
puser += sizeof(void *);
}
tprints("}, ");
}
if (tcp->u_arg[3] == 0)
tprints("NULL, ");
else {
int i;
long nodeuser = tcp->u_arg[3];
tprints("{");
for (i = 0; i < npages; ++i) {
int node;
if (i > 0)
tprints(", ");
if (umove(tcp, nodeuser, &node) < 0) {
tprints("???");
break;
}
tprintf("%#x", node);
nodeuser += sizeof(int);
}
tprints("}, ");
}
}
if (exiting(tcp)) {
unsigned long npages = tcp->u_arg[1];
if (tcp->u_arg[4] == 0)
tprints("NULL, ");
else {
int i;
long statususer = tcp->u_arg[4];
tprints("{");
for (i = 0; i < npages; ++i) {
int status;
if (i > 0)
tprints(", ");
if (umove(tcp, statususer, &status) < 0) {
tprints("???");
break;
}
tprintf("%#x", status);
statususer += sizeof(int);
}
tprints("}, ");
}
printflags(move_pages_flags, tcp->u_arg[5], "MPOL_???");
}
return 0;
}
#if defined(POWERPC)
int
sys_subpage_prot(struct tcb *tcp)
{
if (entering(tcp)) {
unsigned long cur, end, abbrev_end, entries;
unsigned int entry;
tprintf("%#lx, %#lx, ", tcp->u_arg[0], tcp->u_arg[1]);
entries = tcp->u_arg[1] >> 16;
if (!entries || !tcp->u_arg[2]) {
tprints("{}");
return 0;
}
cur = tcp->u_arg[2];
end = cur + (sizeof(int) * entries);
if (!verbose(tcp) || end < tcp->u_arg[2]) {
tprintf("%#lx", tcp->u_arg[2]);
return 0;
}
if (abbrev(tcp)) {
abbrev_end = cur + (sizeof(int) * max_strlen);
if (abbrev_end > end)
abbrev_end = end;
}
else
abbrev_end = end;
tprints("{");
for (; cur < end; cur += sizeof(int)) {
if (cur > tcp->u_arg[2])
tprints(", ");
if (cur >= abbrev_end) {
tprints("...");
break;
}
if (umove(tcp, cur, &entry) < 0) {
tprintf("??? [%#lx]", cur);
break;
}
else
tprintf("%#08x", entry);
}
tprints("}");
}
return 0;
}
#endif