mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-16 15:11:52 +00:00
truss: Add support for utrace(2).
This uses the kdump(1) utrace support code directly until a common library is created. This allows malloc(3) tracing with MALLOC_CONF=utrace:true and rtld tracing with LD_UTRACE=1. Unknown utrace(2) data is just printed as hex. PR: 43819 [inspired by] Reviewed by: jhb MFC after: 2 weeks Relnotes: yes Differential Revision: https://reviews.freebsd.org/D3819
This commit is contained in:
parent
a6fe4c02d6
commit
195aef9962
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=288957
@ -28,7 +28,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd November 1, 2014
|
||||
.Dd October 5, 2015
|
||||
.Dt UTRACE 2
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -70,7 +70,8 @@ support
|
||||
.Sh SEE ALSO
|
||||
.Xr kdump 1 ,
|
||||
.Xr ktrace 1 ,
|
||||
.Xr ktrace 2
|
||||
.Xr ktrace 2 ,
|
||||
.Xr truss 1
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Fn utrace
|
||||
|
@ -6,7 +6,7 @@
|
||||
.PATH: ${.CURDIR}/../ktrace
|
||||
|
||||
PROG= kdump
|
||||
SRCS= kdump_subr.c kdump.c ioctl.c subr.c
|
||||
SRCS= kdump_subr.c kdump.c ioctl.c subr.c utrace.c
|
||||
DPSRCS= kdump_subr.h
|
||||
CFLAGS+= -I${.CURDIR}/../ktrace -I${.CURDIR} -I${.CURDIR}/../.. -I.
|
||||
|
||||
|
@ -67,7 +67,6 @@ extern int errno;
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <ctype.h>
|
||||
#include <dlfcn.h>
|
||||
#include <err.h>
|
||||
#include <grp.h>
|
||||
#include <inttypes.h>
|
||||
@ -117,6 +116,7 @@ void ktrfaultend(struct ktr_faultend *);
|
||||
void limitfd(int fd);
|
||||
void usage(void);
|
||||
void ioctlname(unsigned long, int);
|
||||
int kdump_print_utrace(FILE *, void *, size_t, int);
|
||||
|
||||
#define TIMESTAMP_NONE 0x0
|
||||
#define TIMESTAMP_ABSOLUTE 0x1
|
||||
@ -1536,151 +1536,13 @@ ktrcsw(struct ktr_csw *cs)
|
||||
cs->user ? "user" : "kernel", cs->wmesg);
|
||||
}
|
||||
|
||||
#define UTRACE_DLOPEN_START 1
|
||||
#define UTRACE_DLOPEN_STOP 2
|
||||
#define UTRACE_DLCLOSE_START 3
|
||||
#define UTRACE_DLCLOSE_STOP 4
|
||||
#define UTRACE_LOAD_OBJECT 5
|
||||
#define UTRACE_UNLOAD_OBJECT 6
|
||||
#define UTRACE_ADD_RUNDEP 7
|
||||
#define UTRACE_PRELOAD_FINISHED 8
|
||||
#define UTRACE_INIT_CALL 9
|
||||
#define UTRACE_FINI_CALL 10
|
||||
#define UTRACE_DLSYM_START 11
|
||||
#define UTRACE_DLSYM_STOP 12
|
||||
|
||||
struct utrace_rtld {
|
||||
char sig[4]; /* 'RTLD' */
|
||||
int event;
|
||||
void *handle;
|
||||
void *mapbase;
|
||||
size_t mapsize;
|
||||
int refcnt;
|
||||
char name[MAXPATHLEN];
|
||||
};
|
||||
|
||||
void
|
||||
ktruser_rtld(int len, void *p)
|
||||
{
|
||||
struct utrace_rtld *ut = p;
|
||||
unsigned char *cp;
|
||||
void *parent;
|
||||
int mode;
|
||||
|
||||
switch (ut->event) {
|
||||
case UTRACE_DLOPEN_START:
|
||||
mode = ut->refcnt;
|
||||
printf("dlopen(%s, ", ut->name);
|
||||
switch (mode & RTLD_MODEMASK) {
|
||||
case RTLD_NOW:
|
||||
printf("RTLD_NOW");
|
||||
break;
|
||||
case RTLD_LAZY:
|
||||
printf("RTLD_LAZY");
|
||||
break;
|
||||
default:
|
||||
printf("%#x", mode & RTLD_MODEMASK);
|
||||
}
|
||||
if (mode & RTLD_GLOBAL)
|
||||
printf(" | RTLD_GLOBAL");
|
||||
if (mode & RTLD_TRACE)
|
||||
printf(" | RTLD_TRACE");
|
||||
if (mode & ~(RTLD_MODEMASK | RTLD_GLOBAL | RTLD_TRACE))
|
||||
printf(" | %#x", mode &
|
||||
~(RTLD_MODEMASK | RTLD_GLOBAL | RTLD_TRACE));
|
||||
printf(")\n");
|
||||
break;
|
||||
case UTRACE_DLOPEN_STOP:
|
||||
printf("%p = dlopen(%s) ref %d\n", ut->handle, ut->name,
|
||||
ut->refcnt);
|
||||
break;
|
||||
case UTRACE_DLCLOSE_START:
|
||||
printf("dlclose(%p) (%s, %d)\n", ut->handle, ut->name,
|
||||
ut->refcnt);
|
||||
break;
|
||||
case UTRACE_DLCLOSE_STOP:
|
||||
printf("dlclose(%p) finished\n", ut->handle);
|
||||
break;
|
||||
case UTRACE_LOAD_OBJECT:
|
||||
printf("RTLD: loaded %p @ %p - %p (%s)\n", ut->handle,
|
||||
ut->mapbase, (char *)ut->mapbase + ut->mapsize - 1,
|
||||
ut->name);
|
||||
break;
|
||||
case UTRACE_UNLOAD_OBJECT:
|
||||
printf("RTLD: unloaded %p @ %p - %p (%s)\n", ut->handle,
|
||||
ut->mapbase, (char *)ut->mapbase + ut->mapsize - 1,
|
||||
ut->name);
|
||||
break;
|
||||
case UTRACE_ADD_RUNDEP:
|
||||
parent = ut->mapbase;
|
||||
printf("RTLD: %p now depends on %p (%s, %d)\n", parent,
|
||||
ut->handle, ut->name, ut->refcnt);
|
||||
break;
|
||||
case UTRACE_PRELOAD_FINISHED:
|
||||
printf("RTLD: LD_PRELOAD finished\n");
|
||||
break;
|
||||
case UTRACE_INIT_CALL:
|
||||
printf("RTLD: init %p for %p (%s)\n", ut->mapbase, ut->handle,
|
||||
ut->name);
|
||||
break;
|
||||
case UTRACE_FINI_CALL:
|
||||
printf("RTLD: fini %p for %p (%s)\n", ut->mapbase, ut->handle,
|
||||
ut->name);
|
||||
break;
|
||||
case UTRACE_DLSYM_START:
|
||||
printf("RTLD: dlsym(%p, %s)\n", ut->handle, ut->name);
|
||||
break;
|
||||
case UTRACE_DLSYM_STOP:
|
||||
printf("RTLD: %p = dlsym(%p, %s)\n", ut->mapbase, ut->handle,
|
||||
ut->name);
|
||||
break;
|
||||
default:
|
||||
cp = p;
|
||||
cp += 4;
|
||||
len -= 4;
|
||||
printf("RTLD: %d ", len);
|
||||
while (len--)
|
||||
if (decimal)
|
||||
printf(" %d", *cp++);
|
||||
else
|
||||
printf(" %02x", *cp++);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
struct utrace_malloc {
|
||||
void *p;
|
||||
size_t s;
|
||||
void *r;
|
||||
};
|
||||
|
||||
void
|
||||
ktruser_malloc(void *p)
|
||||
{
|
||||
struct utrace_malloc *ut = p;
|
||||
|
||||
if (ut->p == (void *)(intptr_t)(-1))
|
||||
printf("malloc_init()\n");
|
||||
else if (ut->s == 0)
|
||||
printf("free(%p)\n", ut->p);
|
||||
else if (ut->p == NULL)
|
||||
printf("%p = malloc(%zu)\n", ut->r, ut->s);
|
||||
else
|
||||
printf("%p = realloc(%p, %zu)\n", ut->r, ut->p, ut->s);
|
||||
}
|
||||
|
||||
void
|
||||
ktruser(int len, void *p)
|
||||
{
|
||||
unsigned char *cp;
|
||||
|
||||
if (len >= 8 && bcmp(p, "RTLD", 4) == 0) {
|
||||
ktruser_rtld(len, p);
|
||||
return;
|
||||
}
|
||||
|
||||
if (len == sizeof(struct utrace_malloc)) {
|
||||
ktruser_malloc(p);
|
||||
if (kdump_print_utrace(stdout, p, len, decimal)) {
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
187
usr.bin/kdump/utrace.c
Normal file
187
usr.bin/kdump/utrace.c
Normal file
@ -0,0 +1,187 @@
|
||||
/*-
|
||||
* Copyright (c) 1988, 1993
|
||||
* The Regents of the University of California. 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.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
|
||||
int kdump_print_utrace(FILE *, void *, size_t, int);
|
||||
|
||||
#define UTRACE_DLOPEN_START 1
|
||||
#define UTRACE_DLOPEN_STOP 2
|
||||
#define UTRACE_DLCLOSE_START 3
|
||||
#define UTRACE_DLCLOSE_STOP 4
|
||||
#define UTRACE_LOAD_OBJECT 5
|
||||
#define UTRACE_UNLOAD_OBJECT 6
|
||||
#define UTRACE_ADD_RUNDEP 7
|
||||
#define UTRACE_PRELOAD_FINISHED 8
|
||||
#define UTRACE_INIT_CALL 9
|
||||
#define UTRACE_FINI_CALL 10
|
||||
#define UTRACE_DLSYM_START 11
|
||||
#define UTRACE_DLSYM_STOP 12
|
||||
|
||||
struct utrace_rtld {
|
||||
char sig[4]; /* 'RTLD' */
|
||||
int event;
|
||||
void *handle;
|
||||
void *mapbase;
|
||||
size_t mapsize;
|
||||
int refcnt;
|
||||
char name[MAXPATHLEN];
|
||||
};
|
||||
|
||||
static void
|
||||
print_utrace_rtld(FILE *fp, void *p, size_t len, int decimal)
|
||||
{
|
||||
struct utrace_rtld *ut = p;
|
||||
unsigned char *cp;
|
||||
void *parent;
|
||||
int mode;
|
||||
|
||||
switch (ut->event) {
|
||||
case UTRACE_DLOPEN_START:
|
||||
mode = ut->refcnt;
|
||||
fprintf(fp, "dlopen(%s, ", ut->name);
|
||||
switch (mode & RTLD_MODEMASK) {
|
||||
case RTLD_NOW:
|
||||
fprintf(fp, "RTLD_NOW");
|
||||
break;
|
||||
case RTLD_LAZY:
|
||||
fprintf(fp, "RTLD_LAZY");
|
||||
break;
|
||||
default:
|
||||
fprintf(fp, "%#x", mode & RTLD_MODEMASK);
|
||||
}
|
||||
if (mode & RTLD_GLOBAL)
|
||||
fprintf(fp, " | RTLD_GLOBAL");
|
||||
if (mode & RTLD_TRACE)
|
||||
fprintf(fp, " | RTLD_TRACE");
|
||||
if (mode & ~(RTLD_MODEMASK | RTLD_GLOBAL | RTLD_TRACE))
|
||||
fprintf(fp, " | %#x", mode &
|
||||
~(RTLD_MODEMASK | RTLD_GLOBAL | RTLD_TRACE));
|
||||
fprintf(fp, ")");
|
||||
break;
|
||||
case UTRACE_DLOPEN_STOP:
|
||||
fprintf(fp, "%p = dlopen(%s) ref %d", ut->handle, ut->name,
|
||||
ut->refcnt);
|
||||
break;
|
||||
case UTRACE_DLCLOSE_START:
|
||||
fprintf(fp, "dlclose(%p) (%s, %d)", ut->handle, ut->name,
|
||||
ut->refcnt);
|
||||
break;
|
||||
case UTRACE_DLCLOSE_STOP:
|
||||
fprintf(fp, "dlclose(%p) finished", ut->handle);
|
||||
break;
|
||||
case UTRACE_LOAD_OBJECT:
|
||||
fprintf(fp, "RTLD: loaded %p @ %p - %p (%s)", ut->handle,
|
||||
ut->mapbase, (char *)ut->mapbase + ut->mapsize - 1,
|
||||
ut->name);
|
||||
break;
|
||||
case UTRACE_UNLOAD_OBJECT:
|
||||
fprintf(fp, "RTLD: unloaded %p @ %p - %p (%s)", ut->handle,
|
||||
ut->mapbase, (char *)ut->mapbase + ut->mapsize - 1,
|
||||
ut->name);
|
||||
break;
|
||||
case UTRACE_ADD_RUNDEP:
|
||||
parent = ut->mapbase;
|
||||
fprintf(fp, "RTLD: %p now depends on %p (%s, %d)", parent,
|
||||
ut->handle, ut->name, ut->refcnt);
|
||||
break;
|
||||
case UTRACE_PRELOAD_FINISHED:
|
||||
fprintf(fp, "RTLD: LD_PRELOAD finished");
|
||||
break;
|
||||
case UTRACE_INIT_CALL:
|
||||
fprintf(fp, "RTLD: init %p for %p (%s)", ut->mapbase, ut->handle,
|
||||
ut->name);
|
||||
break;
|
||||
case UTRACE_FINI_CALL:
|
||||
fprintf(fp, "RTLD: fini %p for %p (%s)", ut->mapbase, ut->handle,
|
||||
ut->name);
|
||||
break;
|
||||
case UTRACE_DLSYM_START:
|
||||
fprintf(fp, "RTLD: dlsym(%p, %s)", ut->handle, ut->name);
|
||||
break;
|
||||
case UTRACE_DLSYM_STOP:
|
||||
fprintf(fp, "RTLD: %p = dlsym(%p, %s)", ut->mapbase, ut->handle,
|
||||
ut->name);
|
||||
break;
|
||||
default:
|
||||
cp = p;
|
||||
cp += 4;
|
||||
len -= 4;
|
||||
fprintf(fp, "RTLD: %zu ", len);
|
||||
while (len--)
|
||||
if (decimal)
|
||||
fprintf(fp, " %d", *cp++);
|
||||
else
|
||||
fprintf(fp, " %02x", *cp++);
|
||||
}
|
||||
}
|
||||
|
||||
struct utrace_malloc {
|
||||
void *p;
|
||||
size_t s;
|
||||
void *r;
|
||||
};
|
||||
|
||||
static void
|
||||
print_utrace_malloc(FILE *fp, void *p)
|
||||
{
|
||||
struct utrace_malloc *ut = p;
|
||||
|
||||
if (ut->p == (void *)(intptr_t)(-1))
|
||||
fprintf(fp, "malloc_init()");
|
||||
else if (ut->s == 0)
|
||||
fprintf(fp, "free(%p)", ut->p);
|
||||
else if (ut->p == NULL)
|
||||
fprintf(fp, "%p = malloc(%zu)", ut->r, ut->s);
|
||||
else
|
||||
fprintf(fp, "%p = realloc(%p, %zu)", ut->r, ut->p, ut->s);
|
||||
}
|
||||
|
||||
int
|
||||
kdump_print_utrace(FILE *fp, void *p, size_t len, int decimal)
|
||||
{
|
||||
|
||||
if (len >= 8 && bcmp(p, "RTLD", 4) == 0) {
|
||||
print_utrace_rtld(fp, p, len, decimal);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (len == sizeof(struct utrace_malloc)) {
|
||||
print_utrace_malloc(fp, p);
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
@ -10,6 +10,9 @@ SRCS+= ${MACHINE_ARCH}-fbsd.c
|
||||
SRCS+= ${MACHINE_CPUARCH}-fbsd.c
|
||||
.endif
|
||||
|
||||
.PATH: ${.CURDIR:H}/kdump
|
||||
SRCS+= utrace.c
|
||||
|
||||
CFLAGS+= -I${.CURDIR} -I.
|
||||
CLEANFILES= syscalls.master syscalls.h ioctl.c
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
* Sigprocmask -- the first argument to sigprocmask(). Prints the name.
|
||||
* Kevent -- a pointer to an array of struct kevents. Prints all elements.
|
||||
* Pathconf -- the 2nd argument of pathconf().
|
||||
* Utrace -- utrace(2) buffer.
|
||||
*
|
||||
* In addition, the pointer types (String, Ptr) may have OUT masked in --
|
||||
* this means that the data is set on *return* from the system call -- or
|
||||
@ -43,7 +44,7 @@ enum Argtype { None = 1, Hex, Octal, Int, LongHex, Name, Ptr, Stat, Ioctl, Quad,
|
||||
Fcntlflag, Rusage, BinString, Shutdown, Resource, Rlimit, Timeval2,
|
||||
Pathconf, Rforkflags, ExitStatus, Waitoptions, Idtype, Procctl,
|
||||
LinuxSockArgs, Umtxop, Atfd, Atflags, Timespec2, Accessmode, Long,
|
||||
Sysarch, ExecArgs, ExecEnv, PipeFds, QuadHex };
|
||||
Sysarch, ExecArgs, ExecEnv, PipeFds, QuadHex, Utrace };
|
||||
|
||||
#define ARG_MASK 0xff
|
||||
#define OUT 0x100
|
||||
|
@ -72,6 +72,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include "extern.h"
|
||||
#include "syscall.h"
|
||||
|
||||
/* usr.bin/kdump/utrace.c */
|
||||
int kdump_print_utrace(FILE *, void *, size_t, int);
|
||||
|
||||
/* 64-bit alignment on 32-bit platforms. */
|
||||
#if !defined(__LP64__) && defined(__powerpc__)
|
||||
#define QUAD_ALIGN 1
|
||||
@ -342,6 +345,8 @@ static struct syscall decoded_syscalls[] = {
|
||||
{ Atflags, 3 } } },
|
||||
{ .name = "utimes", .ret_type = 1, .nargs = 2,
|
||||
.args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
|
||||
{ .name = "utrace", .ret_type = 1, .nargs = 1,
|
||||
.args = { { Utrace, 0 } } },
|
||||
{ .name = "wait4", .ret_type = 1, .nargs = 4,
|
||||
.args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 },
|
||||
{ Rusage | OUT, 3 } } },
|
||||
@ -860,6 +865,24 @@ print_kevent(FILE *fp, struct kevent *ke, int input)
|
||||
fprintf(fp, ",%p,%p", (void *)ke->data, (void *)ke->udata);
|
||||
}
|
||||
|
||||
static void
|
||||
print_utrace(FILE *fp, void *utrace_addr, size_t len)
|
||||
{
|
||||
unsigned char *utrace_buffer;
|
||||
|
||||
fprintf(fp, "{ ");
|
||||
if (kdump_print_utrace(fp, utrace_addr, len, 0)) {
|
||||
fprintf(fp, " }");
|
||||
return;
|
||||
}
|
||||
|
||||
utrace_buffer = utrace_addr;
|
||||
fprintf(fp, "%zu:", len);
|
||||
while (len--)
|
||||
fprintf(fp, " %02x", *utrace_buffer++);
|
||||
fprintf(fp, " }");
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a syscall argument into a string. Said string is
|
||||
* allocated via malloc(), so needs to be free()'d. sc is
|
||||
@ -1601,6 +1624,20 @@ print_arg(struct syscall_args *sc, unsigned long *args, long *retval,
|
||||
fprintf(fp, "{ %ld, %ld }", retval[0], retval[1]);
|
||||
retval[0] = 0;
|
||||
break;
|
||||
case Utrace: {
|
||||
size_t len;
|
||||
void *utrace_addr;
|
||||
|
||||
len = args[sc->offset + 1];
|
||||
utrace_addr = calloc(1, len);
|
||||
if (get_struct(pid, (void *)args[sc->offset],
|
||||
(void *)utrace_addr, len) != -1)
|
||||
print_utrace(fp, utrace_addr, len);
|
||||
else
|
||||
fprintf(fp, "0x%lx", args[sc->offset]);
|
||||
free(utrace_addr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd May 12, 2009
|
||||
.Dd October 5, 2015
|
||||
.Dt TRUSS 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -95,7 +95,8 @@ options are mutually exclusive.)
|
||||
.Sh SEE ALSO
|
||||
.Xr kdump 1 ,
|
||||
.Xr ktrace 1 ,
|
||||
.Xr ptrace 2
|
||||
.Xr ptrace 2 ,
|
||||
.Xr utrace 2
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
|
Loading…
Reference in New Issue
Block a user