2013-06-05 16:19:31 +04:00
/*
* Command line utility to exercise the QEMU I / O path .
*
* Copyright ( C ) 2009 Red Hat , Inc .
* Copyright ( c ) 2003 - 2005 Silicon Graphics , Inc .
*
* This work is licensed under the terms of the GNU GPL , version 2 or later .
* See the COPYING file in the top - level directory .
*/
2013-06-05 16:19:39 +04:00
# include "qemu-io.h"
2015-02-05 21:58:22 +03:00
# include "sysemu/block-backend.h"
# include "block/block.h"
# include "block/block_int.h" /* for info_f() */
2013-10-09 12:46:17 +04:00
# include "block/qapi.h"
2015-03-17 20:29:20 +03:00
# include "qemu/error-report.h"
2013-08-21 19:02:47 +04:00
# include "qemu/main-loop.h"
2014-01-15 18:39:10 +04:00
# include "qemu/timer.h"
2015-01-30 05:49:42 +03:00
# include "sysemu/block-backend.h"
2013-06-05 16:19:31 +04:00
# define CMD_NOFILE_OK 0x01
2014-03-06 01:23:00 +04:00
bool qemuio_misalign ;
2013-06-05 16:19:31 +04:00
2013-06-05 16:19:36 +04:00
static cmdinfo_t * cmdtab ;
static int ncmds ;
static int compare_cmdname ( const void * a , const void * b )
{
return strcmp ( ( ( const cmdinfo_t * ) a ) - > name ,
( ( const cmdinfo_t * ) b ) - > name ) ;
}
void qemuio_add_command ( const cmdinfo_t * ci )
{
2014-08-19 12:31:09 +04:00
cmdtab = g_renew ( cmdinfo_t , cmdtab , + + ncmds ) ;
2013-06-05 16:19:36 +04:00
cmdtab [ ncmds - 1 ] = * ci ;
qsort ( cmdtab , ncmds , sizeof ( * cmdtab ) , compare_cmdname ) ;
}
int qemuio_command_usage ( const cmdinfo_t * ci )
{
printf ( " %s %s -- %s \n " , ci - > name , ci - > args , ci - > oneline ) ;
return 0 ;
}
2015-02-05 21:58:22 +03:00
static int init_check_command ( BlockBackend * blk , const cmdinfo_t * ct )
2013-06-05 16:19:36 +04:00
{
if ( ct - > flags & CMD_FLAG_GLOBAL ) {
return 1 ;
}
2015-02-05 21:58:22 +03:00
if ( ! ( ct - > flags & CMD_NOFILE_OK ) & & ! blk ) {
2013-06-05 16:19:36 +04:00
fprintf ( stderr , " no file open, try 'help open' \n " ) ;
return 0 ;
}
return 1 ;
}
2015-02-05 21:58:22 +03:00
static int command ( BlockBackend * blk , const cmdinfo_t * ct , int argc ,
2013-06-05 16:19:39 +04:00
char * * argv )
2013-06-05 16:19:36 +04:00
{
char * cmd = argv [ 0 ] ;
2015-02-05 21:58:22 +03:00
if ( ! init_check_command ( blk , ct ) ) {
2013-06-05 16:19:36 +04:00
return 0 ;
}
if ( argc - 1 < ct - > argmin | | ( ct - > argmax ! = - 1 & & argc - 1 > ct - > argmax ) ) {
if ( ct - > argmax = = - 1 ) {
fprintf ( stderr ,
" bad argument count %d to %s, expected at least %d arguments \n " ,
argc - 1 , cmd , ct - > argmin ) ;
} else if ( ct - > argmin = = ct - > argmax ) {
fprintf ( stderr ,
" bad argument count %d to %s, expected %d arguments \n " ,
argc - 1 , cmd , ct - > argmin ) ;
} else {
fprintf ( stderr ,
" bad argument count %d to %s, expected between %d and %d arguments \n " ,
argc - 1 , cmd , ct - > argmin , ct - > argmax ) ;
}
return 0 ;
}
optind = 0 ;
2015-02-05 21:58:22 +03:00
return ct - > cfunc ( blk , argc , argv ) ;
2013-06-05 16:19:36 +04:00
}
static const cmdinfo_t * find_command ( const char * cmd )
{
cmdinfo_t * ct ;
for ( ct = cmdtab ; ct < & cmdtab [ ncmds ] ; ct + + ) {
if ( strcmp ( ct - > name , cmd ) = = 0 | |
( ct - > altname & & strcmp ( ct - > altname , cmd ) = = 0 ) )
{
return ( const cmdinfo_t * ) ct ;
}
}
return NULL ;
}
2013-11-14 14:54:18 +04:00
/* Invoke fn() for commands with a matching prefix */
void qemuio_complete_command ( const char * input ,
void ( * fn ) ( const char * cmd , void * opaque ) ,
void * opaque )
{
cmdinfo_t * ct ;
size_t input_len = strlen ( input ) ;
for ( ct = cmdtab ; ct < & cmdtab [ ncmds ] ; ct + + ) {
if ( strncmp ( input , ct - > name , input_len ) = = 0 ) {
fn ( ct - > name , opaque ) ;
}
}
}
2013-06-05 16:19:36 +04:00
static char * * breakline ( char * input , int * count )
{
int c = 0 ;
char * p ;
block: Use g_new() & friends where that makes obvious sense
g_new(T, n) is neater than g_malloc(sizeof(T) * n). It's also safer,
for two reasons. One, it catches multiplication overflowing size_t.
Two, it returns T * rather than void *, which lets the compiler catch
more type errors.
Patch created with Coccinelle, with two manual changes on top:
* Add const to bdrv_iterate_format() to keep the types straight
* Convert the allocation in bdrv_drop_intermediate(), which Coccinelle
inexplicably misses
Coccinelle semantic patch:
@@
type T;
@@
-g_malloc(sizeof(T))
+g_new(T, 1)
@@
type T;
@@
-g_try_malloc(sizeof(T))
+g_try_new(T, 1)
@@
type T;
@@
-g_malloc0(sizeof(T))
+g_new0(T, 1)
@@
type T;
@@
-g_try_malloc0(sizeof(T))
+g_try_new0(T, 1)
@@
type T;
expression n;
@@
-g_malloc(sizeof(T) * (n))
+g_new(T, n)
@@
type T;
expression n;
@@
-g_try_malloc(sizeof(T) * (n))
+g_try_new(T, n)
@@
type T;
expression n;
@@
-g_malloc0(sizeof(T) * (n))
+g_new0(T, n)
@@
type T;
expression n;
@@
-g_try_malloc0(sizeof(T) * (n))
+g_try_new0(T, n)
@@
type T;
expression p, n;
@@
-g_realloc(p, sizeof(T) * (n))
+g_renew(T, p, n)
@@
type T;
expression p, n;
@@
-g_try_realloc(p, sizeof(T) * (n))
+g_try_renew(T, p, n)
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2014-08-19 12:31:08 +04:00
char * * rval = g_new0 ( char * , 1 ) ;
2013-06-05 16:19:36 +04:00
while ( rval & & ( p = qemu_strsep ( & input , " " ) ) ! = NULL ) {
if ( ! * p ) {
continue ;
}
c + + ;
2014-08-19 12:31:10 +04:00
rval = g_renew ( char * , rval , ( c + 1 ) ) ;
2013-06-05 16:19:36 +04:00
rval [ c - 1 ] = p ;
rval [ c ] = NULL ;
}
* count = c ;
return rval ;
}
2013-06-05 16:19:31 +04:00
static int64_t cvtnum ( const char * s )
{
char * end ;
2015-09-16 19:02:56 +03:00
return qemu_strtosz_suffix ( s , & end , QEMU_STRTOSZ_DEFSUFFIX_B ) ;
2013-06-05 16:19:31 +04:00
}
2013-06-05 16:19:38 +04:00
# define EXABYTES(x) ((long long)(x) << 60)
# define PETABYTES(x) ((long long)(x) << 50)
# define TERABYTES(x) ((long long)(x) << 40)
# define GIGABYTES(x) ((long long)(x) << 30)
# define MEGABYTES(x) ((long long)(x) << 20)
# define KILOBYTES(x) ((long long)(x) << 10)
# define TO_EXABYTES(x) ((x) / EXABYTES(1))
# define TO_PETABYTES(x) ((x) / PETABYTES(1))
# define TO_TERABYTES(x) ((x) / TERABYTES(1))
# define TO_GIGABYTES(x) ((x) / GIGABYTES(1))
# define TO_MEGABYTES(x) ((x) / MEGABYTES(1))
# define TO_KILOBYTES(x) ((x) / KILOBYTES(1))
static void cvtstr ( double value , char * str , size_t size )
{
char * trim ;
const char * suffix ;
if ( value > = EXABYTES ( 1 ) ) {
suffix = " EiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_EXABYTES ( value ) ) ;
} else if ( value > = PETABYTES ( 1 ) ) {
suffix = " PiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_PETABYTES ( value ) ) ;
} else if ( value > = TERABYTES ( 1 ) ) {
suffix = " TiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_TERABYTES ( value ) ) ;
} else if ( value > = GIGABYTES ( 1 ) ) {
suffix = " GiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_GIGABYTES ( value ) ) ;
} else if ( value > = MEGABYTES ( 1 ) ) {
suffix = " MiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_MEGABYTES ( value ) ) ;
} else if ( value > = KILOBYTES ( 1 ) ) {
suffix = " KiB " ;
snprintf ( str , size - 4 , " %.3f " , TO_KILOBYTES ( value ) ) ;
} else {
suffix = " bytes " ;
snprintf ( str , size - 6 , " %f " , value ) ;
}
trim = strstr ( str , " .000 " ) ;
if ( trim ) {
strcpy ( trim , suffix ) ;
} else {
strcat ( str , suffix ) ;
}
}
static struct timeval tsub ( struct timeval t1 , struct timeval t2 )
{
t1 . tv_usec - = t2 . tv_usec ;
if ( t1 . tv_usec < 0 ) {
t1 . tv_usec + = 1000000 ;
t1 . tv_sec - - ;
}
t1 . tv_sec - = t2 . tv_sec ;
return t1 ;
}
static double tdiv ( double value , struct timeval tv )
{
return value / ( ( double ) tv . tv_sec + ( ( double ) tv . tv_usec / 1000000.0 ) ) ;
}
# define HOURS(sec) ((sec) / (60 * 60))
# define MINUTES(sec) (((sec) % (60 * 60)) / 60)
# define SECONDS(sec) ((sec) % 60)
enum {
DEFAULT_TIME = 0x0 ,
TERSE_FIXED_TIME = 0x1 ,
VERBOSE_FIXED_TIME = 0x2 ,
} ;
static void timestr ( struct timeval * tv , char * ts , size_t size , int format )
{
double usec = ( double ) tv - > tv_usec / 1000000.0 ;
if ( format & TERSE_FIXED_TIME ) {
if ( ! HOURS ( tv - > tv_sec ) ) {
snprintf ( ts , size , " %u:%02u.%02u " ,
( unsigned int ) MINUTES ( tv - > tv_sec ) ,
( unsigned int ) SECONDS ( tv - > tv_sec ) ,
( unsigned int ) ( usec * 100 ) ) ;
return ;
}
format | = VERBOSE_FIXED_TIME ; /* fallback if hours needed */
}
if ( ( format & VERBOSE_FIXED_TIME ) | | tv - > tv_sec ) {
snprintf ( ts , size , " %u:%02u:%02u.%02u " ,
( unsigned int ) HOURS ( tv - > tv_sec ) ,
( unsigned int ) MINUTES ( tv - > tv_sec ) ,
( unsigned int ) SECONDS ( tv - > tv_sec ) ,
( unsigned int ) ( usec * 100 ) ) ;
} else {
snprintf ( ts , size , " 0.%04u sec " , ( unsigned int ) ( usec * 10000 ) ) ;
}
}
2013-06-05 16:19:31 +04:00
/*
* Parse the pattern argument to various sub - commands .
*
* Because the pattern is used as an argument to memset it must evaluate
* to an unsigned integer that fits into a single byte .
*/
static int parse_pattern ( const char * arg )
{
char * endptr = NULL ;
long pattern ;
pattern = strtol ( arg , & endptr , 0 ) ;
if ( pattern < 0 | | pattern > UCHAR_MAX | | * endptr ! = ' \0 ' ) {
printf ( " %s is not a valid pattern byte \n " , arg ) ;
return - 1 ;
}
return pattern ;
}
/*
* Memory allocation helpers .
*
* Make sure memory is aligned by default , or purposefully misaligned if
* that is specified on the command line .
*/
# define MISALIGN_OFFSET 16
2015-02-05 21:58:22 +03:00
static void * qemu_io_alloc ( BlockBackend * blk , size_t len , int pattern )
2013-06-05 16:19:31 +04:00
{
void * buf ;
if ( qemuio_misalign ) {
len + = MISALIGN_OFFSET ;
}
2015-02-05 21:58:22 +03:00
buf = blk_blockalign ( blk , len ) ;
2013-06-05 16:19:31 +04:00
memset ( buf , pattern , len ) ;
if ( qemuio_misalign ) {
buf + = MISALIGN_OFFSET ;
}
return buf ;
}
static void qemu_io_free ( void * p )
{
if ( qemuio_misalign ) {
p - = MISALIGN_OFFSET ;
}
qemu_vfree ( p ) ;
}
static void dump_buffer ( const void * buffer , int64_t offset , int len )
{
int i , j ;
const uint8_t * p ;
for ( i = 0 , p = buffer ; i < len ; i + = 16 ) {
const uint8_t * s = p ;
printf ( " %08 " PRIx64 " : " , offset + i ) ;
for ( j = 0 ; j < 16 & & i + j < len ; j + + , p + + ) {
printf ( " %02x " , * p ) ;
}
printf ( " " ) ;
for ( j = 0 ; j < 16 & & i + j < len ; j + + , s + + ) {
if ( isalnum ( * s ) ) {
printf ( " %c " , * s ) ;
} else {
printf ( " . " ) ;
}
}
printf ( " \n " ) ;
}
}
static void print_report ( const char * op , struct timeval * t , int64_t offset ,
int count , int total , int cnt , int Cflag )
{
char s1 [ 64 ] , s2 [ 64 ] , ts [ 64 ] ;
timestr ( t , ts , sizeof ( ts ) , Cflag ? VERBOSE_FIXED_TIME : 0 ) ;
if ( ! Cflag ) {
cvtstr ( ( double ) total , s1 , sizeof ( s1 ) ) ;
cvtstr ( tdiv ( ( double ) total , * t ) , s2 , sizeof ( s2 ) ) ;
printf ( " %s %d/%d bytes at offset % " PRId64 " \n " ,
op , total , count , offset ) ;
printf ( " %s, %d ops; %s (%s/sec and %.4f ops/sec) \n " ,
s1 , cnt , ts , s2 , tdiv ( ( double ) cnt , * t ) ) ;
} else { /* bytes,ops,time,bytes/sec,ops/sec */
printf ( " %d,%d,%s,%.3f,%.3f \n " ,
total , cnt , ts ,
tdiv ( ( double ) total , * t ) ,
tdiv ( ( double ) cnt , * t ) ) ;
}
}
/*
* Parse multiple length statements for vectored I / O , and construct an I / O
* vector matching it .
*/
static void *
2015-02-05 21:58:22 +03:00
create_iovec ( BlockBackend * blk , QEMUIOVector * qiov , char * * argv , int nr_iov ,
2013-06-05 16:19:31 +04:00
int pattern )
{
size_t * sizes = g_new0 ( size_t , nr_iov ) ;
size_t count = 0 ;
void * buf = NULL ;
void * p ;
int i ;
for ( i = 0 ; i < nr_iov ; i + + ) {
char * arg = argv [ i ] ;
int64_t len ;
len = cvtnum ( arg ) ;
if ( len < 0 ) {
printf ( " non-numeric length argument -- %s \n " , arg ) ;
goto fail ;
}
/* should be SIZE_T_MAX, but that doesn't exist */
if ( len > INT_MAX ) {
printf ( " too large length argument -- %s \n " , arg ) ;
goto fail ;
}
if ( len & 0x1ff ) {
printf ( " length argument % " PRId64
" is not sector aligned \n " , len ) ;
goto fail ;
}
sizes [ i ] = len ;
count + = len ;
}
qemu_iovec_init ( qiov , nr_iov ) ;
2015-02-05 21:58:22 +03:00
buf = p = qemu_io_alloc ( blk , count , pattern ) ;
2013-06-05 16:19:31 +04:00
for ( i = 0 ; i < nr_iov ; i + + ) {
qemu_iovec_add ( qiov , p , sizes [ i ] ) ;
p + = sizes [ i ] ;
}
fail :
g_free ( sizes ) ;
return buf ;
}
2015-02-05 21:58:22 +03:00
static int do_read ( BlockBackend * blk , char * buf , int64_t offset , int count ,
2013-06-05 16:19:31 +04:00
int * total )
{
int ret ;
2015-02-05 21:58:22 +03:00
ret = blk_read ( blk , offset > > 9 , ( uint8_t * ) buf , count > > 9 ) ;
2013-06-05 16:19:31 +04:00
if ( ret < 0 ) {
return ret ;
}
* total = count ;
return 1 ;
}
2015-02-05 21:58:22 +03:00
static int do_write ( BlockBackend * blk , char * buf , int64_t offset , int count ,
2013-06-05 16:19:31 +04:00
int * total )
{
int ret ;
2015-02-05 21:58:22 +03:00
ret = blk_write ( blk , offset > > 9 , ( uint8_t * ) buf , count > > 9 ) ;
2013-06-05 16:19:31 +04:00
if ( ret < 0 ) {
return ret ;
}
* total = count ;
return 1 ;
}
2015-02-05 21:58:22 +03:00
static int do_pread ( BlockBackend * blk , char * buf , int64_t offset , int count ,
2013-06-05 16:19:31 +04:00
int * total )
{
2015-02-05 21:58:22 +03:00
* total = blk_pread ( blk , offset , ( uint8_t * ) buf , count ) ;
2013-06-05 16:19:31 +04:00
if ( * total < 0 ) {
return * total ;
}
return 1 ;
}
2015-02-05 21:58:22 +03:00
static int do_pwrite ( BlockBackend * blk , char * buf , int64_t offset , int count ,
2013-06-05 16:19:31 +04:00
int * total )
{
2015-02-05 21:58:22 +03:00
* total = blk_pwrite ( blk , offset , ( uint8_t * ) buf , count ) ;
2013-06-05 16:19:31 +04:00
if ( * total < 0 ) {
return * total ;
}
return 1 ;
}
typedef struct {
2015-02-05 21:58:22 +03:00
BlockBackend * blk ;
2013-06-05 16:19:31 +04:00
int64_t offset ;
int count ;
int * total ;
int ret ;
bool done ;
} CoWriteZeroes ;
static void coroutine_fn co_write_zeroes_entry ( void * opaque )
{
CoWriteZeroes * data = opaque ;
2015-02-05 21:58:22 +03:00
data - > ret = blk_co_write_zeroes ( data - > blk , data - > offset / BDRV_SECTOR_SIZE ,
data - > count / BDRV_SECTOR_SIZE , 0 ) ;
2013-06-05 16:19:31 +04:00
data - > done = true ;
if ( data - > ret < 0 ) {
* data - > total = data - > ret ;
return ;
}
* data - > total = data - > count ;
}
2015-02-05 21:58:22 +03:00
static int do_co_write_zeroes ( BlockBackend * blk , int64_t offset , int count ,
2013-06-05 16:19:31 +04:00
int * total )
{
Coroutine * co ;
CoWriteZeroes data = {
2015-02-05 21:58:22 +03:00
. blk = blk ,
2013-06-05 16:19:31 +04:00
. offset = offset ,
. count = count ,
. total = total ,
. done = false ,
} ;
co = qemu_coroutine_create ( co_write_zeroes_entry ) ;
qemu_coroutine_enter ( co , & data ) ;
while ( ! data . done ) {
2015-02-05 21:58:22 +03:00
aio_poll ( blk_get_aio_context ( blk ) , true ) ;
2013-06-05 16:19:31 +04:00
}
if ( data . ret < 0 ) {
return data . ret ;
} else {
return 1 ;
}
}
2015-02-05 21:58:22 +03:00
static int do_write_compressed ( BlockBackend * blk , char * buf , int64_t offset ,
2013-06-05 16:19:31 +04:00
int count , int * total )
{
int ret ;
2015-02-05 21:58:22 +03:00
ret = blk_write_compressed ( blk , offset > > 9 , ( uint8_t * ) buf , count > > 9 ) ;
2013-06-05 16:19:31 +04:00
if ( ret < 0 ) {
return ret ;
}
* total = count ;
return 1 ;
}
2015-02-05 21:58:22 +03:00
static int do_load_vmstate ( BlockBackend * blk , char * buf , int64_t offset ,
2013-06-05 16:19:31 +04:00
int count , int * total )
{
2015-02-05 21:58:22 +03:00
* total = blk_load_vmstate ( blk , ( uint8_t * ) buf , offset , count ) ;
2013-06-05 16:19:31 +04:00
if ( * total < 0 ) {
return * total ;
}
return 1 ;
}
2015-02-05 21:58:22 +03:00
static int do_save_vmstate ( BlockBackend * blk , char * buf , int64_t offset ,
2013-06-05 16:19:31 +04:00
int count , int * total )
{
2015-02-05 21:58:22 +03:00
* total = blk_save_vmstate ( blk , ( uint8_t * ) buf , offset , count ) ;
2013-06-05 16:19:31 +04:00
if ( * total < 0 ) {
return * total ;
}
return 1 ;
}
# define NOT_DONE 0x7fffffff
static void aio_rw_done ( void * opaque , int ret )
{
* ( int * ) opaque = ret ;
}
2015-02-05 21:58:22 +03:00
static int do_aio_readv ( BlockBackend * blk , QEMUIOVector * qiov ,
2013-06-05 16:19:31 +04:00
int64_t offset , int * total )
{
int async_ret = NOT_DONE ;
2015-02-05 21:58:22 +03:00
blk_aio_readv ( blk , offset > > 9 , qiov , qiov - > size > > 9 ,
aio_rw_done , & async_ret ) ;
2013-06-05 16:19:31 +04:00
while ( async_ret = = NOT_DONE ) {
main_loop_wait ( false ) ;
}
* total = qiov - > size ;
return async_ret < 0 ? async_ret : 1 ;
}
2015-02-05 21:58:22 +03:00
static int do_aio_writev ( BlockBackend * blk , QEMUIOVector * qiov ,
2013-06-05 16:19:31 +04:00
int64_t offset , int * total )
{
int async_ret = NOT_DONE ;
2015-02-05 21:58:22 +03:00
blk_aio_writev ( blk , offset > > 9 , qiov , qiov - > size > > 9 ,
aio_rw_done , & async_ret ) ;
2013-06-05 16:19:31 +04:00
while ( async_ret = = NOT_DONE ) {
main_loop_wait ( false ) ;
}
* total = qiov - > size ;
return async_ret < 0 ? async_ret : 1 ;
}
struct multiwrite_async_ret {
int num_done ;
int error ;
} ;
static void multiwrite_cb ( void * opaque , int ret )
{
struct multiwrite_async_ret * async_ret = opaque ;
async_ret - > num_done + + ;
if ( ret < 0 ) {
async_ret - > error = ret ;
}
}
2015-02-05 21:58:22 +03:00
static int do_aio_multiwrite ( BlockBackend * blk , BlockRequest * reqs ,
2013-06-05 16:19:31 +04:00
int num_reqs , int * total )
{
int i , ret ;
struct multiwrite_async_ret async_ret = {
. num_done = 0 ,
. error = 0 ,
} ;
* total = 0 ;
for ( i = 0 ; i < num_reqs ; i + + ) {
reqs [ i ] . cb = multiwrite_cb ;
reqs [ i ] . opaque = & async_ret ;
* total + = reqs [ i ] . qiov - > size ;
}
2015-02-05 21:58:22 +03:00
ret = blk_aio_multiwrite ( blk , reqs , num_reqs ) ;
2013-06-05 16:19:31 +04:00
if ( ret < 0 ) {
return ret ;
}
while ( async_ret . num_done < num_reqs ) {
main_loop_wait ( false ) ;
}
return async_ret . error < 0 ? async_ret . error : 1 ;
}
static void read_help ( void )
{
printf (
" \n "
" reads a range of bytes from the given offset \n "
" \n "
" Example: \n "
" 'read -v 512 1k' - dumps 1 kilobyte read from 512 bytes into the file \n "
" \n "
" Reads a segment of the currently open file, optionally dumping it to the \n "
" standard output stream (with -v option) for subsequent inspection. \n "
" -b, -- read from the VM state rather than the virtual disk \n "
" -C, -- report statistics in a machine parsable format \n "
" -l, -- length for pattern verification (only with -P) \n "
2015-02-05 21:58:22 +03:00
" -p, -- use blk_pread to read the file \n "
2013-06-05 16:19:31 +04:00
" -P, -- use a pattern to verify read data \n "
" -q, -- quiet mode, do not show I/O statistics \n "
" -s, -- start offset for pattern verification (only with -P) \n "
" -v, -- dump buffer to standard output \n "
" \n " ) ;
}
2015-02-05 21:58:22 +03:00
static int read_f ( BlockBackend * blk , int argc , char * * argv ) ;
2013-06-05 16:19:31 +04:00
static const cmdinfo_t read_cmd = {
. name = " read " ,
. altname = " r " ,
. cfunc = read_f ,
. argmin = 2 ,
. argmax = - 1 ,
. args = " [-abCpqv] [-P pattern [-s off] [-l len]] off len " ,
. oneline = " reads a number of bytes at a specified offset " ,
. help = read_help ,
} ;
2015-02-05 21:58:22 +03:00
static int read_f ( BlockBackend * blk , int argc , char * * argv )
2013-06-05 16:19:31 +04:00
{
struct timeval t1 , t2 ;
int Cflag = 0 , pflag = 0 , qflag = 0 , vflag = 0 ;
int Pflag = 0 , sflag = 0 , lflag = 0 , bflag = 0 ;
int c , cnt ;
char * buf ;
int64_t offset ;
int count ;
/* Some compilers get confused and warn if this is not initialized. */
int total = 0 ;
int pattern = 0 , pattern_offset = 0 , pattern_count = 0 ;
2015-05-12 18:10:56 +03:00
while ( ( c = getopt ( argc , argv , " bCl:pP:qs:v " ) ) ! = - 1 ) {
2013-06-05 16:19:31 +04:00
switch ( c ) {
case ' b ' :
bflag = 1 ;
break ;
case ' C ' :
Cflag = 1 ;
break ;
case ' l ' :
lflag = 1 ;
pattern_count = cvtnum ( optarg ) ;
if ( pattern_count < 0 ) {
printf ( " non-numeric length argument -- %s \n " , optarg ) ;
return 0 ;
}
break ;
case ' p ' :
pflag = 1 ;
break ;
case ' P ' :
Pflag = 1 ;
pattern = parse_pattern ( optarg ) ;
if ( pattern < 0 ) {
return 0 ;
}
break ;
case ' q ' :
qflag = 1 ;
break ;
case ' s ' :
sflag = 1 ;
pattern_offset = cvtnum ( optarg ) ;
if ( pattern_offset < 0 ) {
printf ( " non-numeric length argument -- %s \n " , optarg ) ;
return 0 ;
}
break ;
case ' v ' :
vflag = 1 ;
break ;
default :
2013-06-05 16:19:36 +04:00
return qemuio_command_usage ( & read_cmd ) ;
2013-06-05 16:19:31 +04:00
}
}
if ( optind ! = argc - 2 ) {
2013-06-05 16:19:36 +04:00
return qemuio_command_usage ( & read_cmd ) ;
2013-06-05 16:19:31 +04:00
}
if ( bflag & & pflag ) {
printf ( " -b and -p cannot be specified at the same time \n " ) ;
return 0 ;
}
offset = cvtnum ( argv [ optind ] ) ;
if ( offset < 0 ) {
printf ( " non-numeric length argument -- %s \n " , argv [ optind ] ) ;
return 0 ;
}
optind + + ;
count = cvtnum ( argv [ optind ] ) ;
if ( count < 0 ) {
printf ( " non-numeric length argument -- %s \n " , argv [ optind ] ) ;
return 0 ;
}
if ( ! Pflag & & ( lflag | | sflag ) ) {
2013-06-05 16:19:36 +04:00
return qemuio_command_usage ( & read_cmd ) ;
2013-06-05 16:19:31 +04:00
}
if ( ! lflag ) {
pattern_count = count - pattern_offset ;
}
if ( ( pattern_count < 0 ) | | ( pattern_count + pattern_offset > count ) ) {
printf ( " pattern verification range exceeds end of read data \n " ) ;
return 0 ;
}
if ( ! pflag ) {
if ( offset & 0x1ff ) {
printf ( " offset % " PRId64 " is not sector aligned \n " ,
offset ) ;
return 0 ;
}
if ( count & 0x1ff ) {
printf ( " count %d is not sector aligned \n " ,
count ) ;
return 0 ;
}
}
2015-02-05 21:58:22 +03:00
buf = qemu_io_alloc ( blk , count , 0xab ) ;
2013-06-05 16:19:31 +04:00
gettimeofday ( & t1 , NULL ) ;
if ( pflag ) {
2015-02-05 21:58:22 +03:00
cnt = do_pread ( blk , buf , offset , count , & total ) ;
2013-06-05 16:19:31 +04:00
} else if ( bflag ) {
2015-02-05 21:58:22 +03:00
cnt = do_load_vmstate ( blk , buf , offset , count , & total ) ;
2013-06-05 16:19:31 +04:00
} else {
2015-02-05 21:58:22 +03:00
cnt = do_read ( blk , buf , offset , count , & total ) ;
2013-06-05 16:19:31 +04:00
}
gettimeofday ( & t2 , NULL ) ;
if ( cnt < 0 ) {
printf ( " read failed: %s \n " , strerror ( - cnt ) ) ;
goto out ;
}
if ( Pflag ) {
void * cmp_buf = g_malloc ( pattern_count ) ;
memset ( cmp_buf , pattern , pattern_count ) ;
if ( memcmp ( buf + pattern_offset , cmp_buf , pattern_count ) ) {
printf ( " Pattern verification failed at offset % "
PRId64 " , %d bytes \n " ,
offset + pattern_offset , pattern_count ) ;
}
g_free ( cmp_buf ) ;
}
if ( qflag ) {
goto out ;
}
if ( vflag ) {
dump_buffer ( buf , offset , count ) ;
}
/* Finally, report back -- -C gives a parsable format */
t2 = tsub ( t2 , t1 ) ;
print_report ( " read " , & t2 , offset , count , total , cnt , Cflag ) ;
out :
qemu_io_free ( buf ) ;
return 0 ;
}
static void readv_help ( void )
{
printf (
" \n "
" reads a range of bytes from the given offset into multiple buffers \n "
" \n "
" Example: \n "
" 'readv -v 512 1k 1k ' - dumps 2 kilobytes read from 512 bytes into the file \n "
" \n "
" Reads a segment of the currently open file, optionally dumping it to the \n "
" standard output stream (with -v option) for subsequent inspection. \n "
" Uses multiple iovec buffers if more than one byte range is specified. \n "
" -C, -- report statistics in a machine parsable format \n "
" -P, -- use a pattern to verify read data \n "
" -v, -- dump buffer to standard output \n "
" -q, -- quiet mode, do not show I/O statistics \n "
" \n " ) ;
}
2015-02-05 21:58:22 +03:00
static int readv_f ( BlockBackend * blk , int argc , char * * argv ) ;
2013-06-05 16:19:31 +04:00
static const cmdinfo_t readv_cmd = {
. name = " readv " ,
. cfunc = readv_f ,
. argmin = 2 ,
. argmax = - 1 ,
. args = " [-Cqv] [-P pattern ] off len [len..] " ,
. oneline = " reads a number of bytes at a specified offset " ,
. help = readv_help ,
} ;
2015-02-05 21:58:22 +03:00
static int readv_f ( BlockBackend * blk , int argc , char * * argv )
2013-06-05 16:19:31 +04:00
{
struct timeval t1 , t2 ;
int Cflag = 0 , qflag = 0 , vflag = 0 ;
int c , cnt ;
char * buf ;
int64_t offset ;
/* Some compilers get confused and warn if this is not initialized. */
int total = 0 ;