strace/scsi.c
Eugene Syromyatnikov 030b61c92f io: use umoven_or_printaddr_ignore_syserror as umove function in tprint_iov_upto
This enables printing of iovec arrays even in case of failed syscall
(failed syscall doesn't mean that iovec itself is inaccessible and
useless).  One caveat here is that we should explicitly provide proper
IOV_DECODE_* value based on syserror value in case printing is performed
on exiting; we can't simply override it to IOV_DECODE_ADDR on exiting
when syserror is up, since this may be called by code which tries to
print iovec containing local data, which should be perfectly accessible
(on the other hand, there are no cases of such behaviour at the moment).

Since iovecs themselves are printed even if syscall has failed now,
preadv test is updated to reflect this.  It is notable, though, that this
is the only place where this case is checked.

* io.c (tprint_iov_upto): Specify umoven_or_printaddr_ignore_syserror
instead of umoven_or_printaddr as umoven_func parameter.
(SYS_FUNC(readv), do_preadv): Specify decode_iov parameter value
based on syserror(tcp) value.
* scsi.c: (print_sg_io_v3_res, print_sg_io_v4_res): Likewise.
* tests/preadv.c: Update expected output for the case when preadv
with singe-item iovec failed.
2016-10-28 18:40:11 +00:00

285 lines
7.9 KiB
C

/*
* Copyright (c) 2007 Vladimir Nadvornik <nadvornik@suse.cz>
* Copyright (c) 2007 Dmitry V. Levin <ldv@altlinux.org>
* 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"
#ifdef HAVE_SCSI_SG_H
# include <linux/ioctl.h>
# include <scsi/sg.h>
# include "xlat/sg_io_dxfer_direction.h"
# ifdef HAVE_LINUX_BSG_H
# include <linux/bsg.h>
# include <sys/uio.h>
# include "xlat/bsg_protocol.h"
# include "xlat/bsg_subprotocol.h"
# endif
static bool
print_uchar(struct tcb *tcp, void *elem_buf, size_t elem_size, void *data)
{
tprintf("%02x", (unsigned int) (* (unsigned char *) elem_buf));
return true;
}
static void
print_sg_io_buffer(struct tcb *tcp, const unsigned long addr, const unsigned int len)
{
unsigned char buf;
print_array(tcp, addr, len, &buf, sizeof(buf),
umoven_or_printaddr, print_uchar, 0);
}
static int
print_sg_io_v3_req(struct tcb *tcp, const long arg)
{
struct sg_io_hdr sg_io;
if (umove(tcp, arg, &sg_io) < 0) {
tprints("???}");
return RVAL_DECODED | 1;
}
printxval(sg_io_dxfer_direction, sg_io.dxfer_direction,
"SG_DXFER_???");
tprintf(", cmd[%u]=", sg_io.cmd_len);
print_sg_io_buffer(tcp, (unsigned long) sg_io.cmdp, sg_io.cmd_len);
tprintf(", mx_sb_len=%d", sg_io.mx_sb_len);
tprintf(", iovec_count=%d", sg_io.iovec_count);
tprintf(", dxfer_len=%u", sg_io.dxfer_len);
tprintf(", timeout=%u", sg_io.timeout);
tprintf(", flags=%#x", sg_io.flags);
if (sg_io.dxfer_direction == SG_DXFER_TO_DEV ||
sg_io.dxfer_direction == SG_DXFER_TO_FROM_DEV) {
tprintf(", data[%u]=", sg_io.dxfer_len);
if (sg_io.iovec_count)
tprint_iov_upto(tcp, sg_io.iovec_count,
(unsigned long) sg_io.dxferp,
IOV_DECODE_STR,
sg_io.dxfer_len);
else
print_sg_io_buffer(tcp, (unsigned long) sg_io.dxferp,
sg_io.dxfer_len);
}
return 1;
}
static void
print_sg_io_v3_res(struct tcb *tcp, const long arg)
{
struct sg_io_hdr sg_io;
if (umove(tcp, arg, &sg_io) < 0) {
tprints(", ???");
return;
}
if (sg_io.dxfer_direction == SG_DXFER_FROM_DEV ||
sg_io.dxfer_direction == SG_DXFER_TO_FROM_DEV) {
uint32_t din_len = sg_io.dxfer_len;
if (sg_io.resid > 0)
din_len -= sg_io.resid;
tprintf(", data[%u]=", din_len);
if (sg_io.iovec_count)
tprint_iov_upto(tcp, sg_io.iovec_count,
(unsigned long) sg_io.dxferp,
syserror(tcp) ? IOV_DECODE_ADDR :
IOV_DECODE_STR, din_len);
else
print_sg_io_buffer(tcp, (unsigned long) sg_io.dxferp,
din_len);
}
tprintf(", status=%02x", sg_io.status);
tprintf(", masked_status=%02x", sg_io.masked_status);
tprintf(", sb[%u]=", sg_io.sb_len_wr);
print_sg_io_buffer(tcp, (unsigned long) sg_io.sbp, sg_io.sb_len_wr);
tprintf(", host_status=%#x", sg_io.host_status);
tprintf(", driver_status=%#x", sg_io.driver_status);
tprintf(", resid=%d", sg_io.resid);
tprintf(", duration=%d", sg_io.duration);
tprintf(", info=%#x", sg_io.info);
}
#ifdef HAVE_LINUX_BSG_H
static int
print_sg_io_v4_req(struct tcb *tcp, const long arg)
{
struct sg_io_v4 sg_io;
if (umove(tcp, arg, &sg_io) < 0) {
tprints("???}");
return RVAL_DECODED | 1;
}
printxval(bsg_protocol, sg_io.protocol, "BSG_PROTOCOL_???");
tprints(", ");
printxval(bsg_subprotocol, sg_io.subprotocol, "BSG_SUB_PROTOCOL_???");
tprintf(", request[%u]=", sg_io.request_len);
print_sg_io_buffer(tcp, sg_io.request, sg_io.request_len);
tprintf(", request_tag=%" PRI__u64, sg_io.request_tag);
tprintf(", request_attr=%u", sg_io.request_attr);
tprintf(", request_priority=%u", sg_io.request_priority);
tprintf(", request_extra=%u", sg_io.request_extra);
tprintf(", max_response_len=%u", sg_io.max_response_len);
tprintf(", dout_iovec_count=%u", sg_io.dout_iovec_count);
tprintf(", dout_xfer_len=%u", sg_io.dout_xfer_len);
tprintf(", din_iovec_count=%u", sg_io.din_iovec_count);
tprintf(", din_xfer_len=%u", sg_io.din_xfer_len);
tprintf(", timeout=%u", sg_io.timeout);
tprintf(", flags=%u", sg_io.flags);
tprintf(", usr_ptr=%" PRI__u64, sg_io.usr_ptr);
tprintf(", spare_in=%u", sg_io.spare_in);
tprintf(", dout[%u]=", sg_io.dout_xfer_len);
if (sg_io.dout_iovec_count)
tprint_iov_upto(tcp, sg_io.dout_iovec_count, sg_io.dout_xferp,
IOV_DECODE_STR, sg_io.dout_xfer_len);
else
print_sg_io_buffer(tcp, sg_io.dout_xferp, sg_io.dout_xfer_len);
return 1;
}
static void
print_sg_io_v4_res(struct tcb *tcp, const long arg)
{
struct sg_io_v4 sg_io;
uint32_t din_len;
if (umove(tcp, arg, &sg_io) < 0) {
tprints(", ???");
return;
}
tprintf(", response[%u]=", sg_io.response_len);
print_sg_io_buffer(tcp, sg_io.response, sg_io.response_len);
din_len = sg_io.din_xfer_len;
if (sg_io.din_resid > 0)
din_len -= sg_io.din_resid;
tprintf(", din[%u]=", din_len);
if (sg_io.din_iovec_count)
tprint_iov_upto(tcp, sg_io.din_iovec_count, sg_io.din_xferp,
syserror(tcp) ? IOV_DECODE_ADDR :
IOV_DECODE_STR, din_len);
else
print_sg_io_buffer(tcp, sg_io.din_xferp, din_len);
tprintf(", driver_status=%u", sg_io.driver_status);
tprintf(", transport_status=%u", sg_io.transport_status);
tprintf(", device_status=%u", sg_io.device_status);
tprintf(", retry_delay=%u", sg_io.retry_delay);
tprintf(", info=%u", sg_io.info);
tprintf(", duration=%u", sg_io.duration);
tprintf(", response_len=%u", sg_io.response_len);
tprintf(", din_resid=%u", sg_io.din_resid);
tprintf(", dout_resid=%u", sg_io.dout_resid);
tprintf(", generated_tag=%" PRI__u64, sg_io.generated_tag);
tprintf(", spare_out=%u", sg_io.spare_out);
}
#else /* !HAVE_LINUX_BSG_H */
static int
print_sg_io_v4_req(struct tcb *tcp, const long arg)
{
tprints("...}");
return RVAL_DECODED | 1;
}
static void
print_sg_io_v4_res(struct tcb *tcp, const long arg)
{
}
#endif
static int
print_sg_io_req(struct tcb *tcp, uint32_t iid, const long arg)
{
tprintf("{'%c', ", iid);
switch (iid) {
case 'S':
return print_sg_io_v3_req(tcp, arg);
case 'Q':
return print_sg_io_v4_req(tcp, arg);
default:
tprints("...}");
return RVAL_DECODED | 1;
}
}
static void
print_sg_io_res(struct tcb *tcp, uint32_t iid, const long arg)
{
switch (iid) {
case 'S':
print_sg_io_v3_res(tcp, arg);
break;
case 'Q':
print_sg_io_v4_res(tcp, arg);
break;
}
}
int
scsi_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
{
uint32_t iid;
if (SG_IO != code)
return RVAL_DECODED;
if (entering(tcp)) {
tprints(", ");
if (!arg || umove(tcp, arg, &iid) < 0) {
printaddr(arg);
return RVAL_DECODED | 1;
} else {
return print_sg_io_req(tcp, iid, arg);
}
} else {
if (!syserror(tcp)) {
if (umove(tcp, arg, &iid) < 0)
tprints(", ???");
else
print_sg_io_res(tcp, iid, arg);
}
tprints("}");
return RVAL_DECODED | 1;
}
}
#endif /* HAVE_SCSI_SG_H */