2015-08-05 12:40:00 +03:00
/*
* Copyright ( C ) 2015 Red Hat , Inc . All rights reserved .
*
* This file is part of the device - mapper userspace tools .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v .2 .1 .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include "dmlib.h"
2015-08-19 22:39:10 +03:00
# include "math.h" /* log10() */
2015-08-05 12:40:00 +03:00
# define DM_STATS_REGION_NOT_PRESENT UINT64_MAX
2015-08-19 22:39:10 +03:00
# define NSEC_PER_USEC 1000L
2015-08-05 12:40:00 +03:00
# define NSEC_PER_MSEC 1000000L
# define NSEC_PER_SEC 1000000000L
2015-08-20 13:55:06 +03:00
# define PRECISE_ARG "precise_timestamps"
2015-08-19 22:39:10 +03:00
# define HISTOGRAM_ARG "histogram:"
/* Histogram bin */
struct dm_histogram_bin {
uint64_t upper ; /* Upper bound on this bin. */
uint64_t count ; /* Count value for this bin. */
} ;
struct dm_histogram {
/* The stats handle this histogram belongs to. */
const struct dm_stats * dms ;
/* The region this histogram belongs to. */
const struct dm_stats_region * region ;
uint64_t sum ; /* Sum of histogram bin counts. */
int nr_bins ; /* Number of histogram bins assigned. */
struct dm_histogram_bin bins [ 0 ] ;
} ;
2015-08-20 13:55:06 +03:00
2015-08-05 12:40:00 +03:00
/*
* See Documentation / device - mapper / statistics . txt for full descriptions
* of the device - mapper statistics counter fields .
*/
struct dm_stats_counters {
uint64_t reads ; /* Num reads completed */
uint64_t reads_merged ; /* Num reads merged */
uint64_t read_sectors ; /* Num sectors read */
uint64_t read_nsecs ; /* Num milliseconds spent reading */
uint64_t writes ; /* Num writes completed */
uint64_t writes_merged ; /* Num writes merged */
uint64_t write_sectors ; /* Num sectors written */
uint64_t write_nsecs ; /* Num milliseconds spent writing */
uint64_t io_in_progress ; /* Num I/Os currently in progress */
uint64_t io_nsecs ; /* Num milliseconds spent doing I/Os */
uint64_t weighted_io_nsecs ; /* Weighted num milliseconds doing I/Os */
uint64_t total_read_nsecs ; /* Total time spent reading in milliseconds */
uint64_t total_write_nsecs ; /* Total time spent writing in milliseconds */
2015-08-19 22:39:10 +03:00
struct dm_histogram * histogram ; /* Histogram. */
2015-08-05 12:40:00 +03:00
} ;
struct dm_stats_region {
uint64_t region_id ; /* as returned by @stats_list */
uint64_t start ;
uint64_t len ;
uint64_t step ;
char * program_id ;
char * aux_data ;
uint64_t timescale ; /* precise_timestamps is per-region */
2015-08-19 22:39:10 +03:00
struct dm_histogram * bounds ; /* histogram configuration */
2015-08-05 12:40:00 +03:00
struct dm_stats_counters * counters ;
} ;
struct dm_stats {
/* device binding */
int major ; /* device major that this dm_stats object is bound to */
int minor ; /* device minor that this dm_stats object is bound to */
char * name ; /* device-mapper device name */
char * uuid ; /* device-mapper UUID */
char * program_id ; /* default program_id for this handle */
struct dm_pool * mem ; /* memory pool for region and counter tables */
2015-08-19 22:39:10 +03:00
struct dm_pool * hist_mem ; /* separate pool for histogram tables */
2015-08-05 12:40:00 +03:00
uint64_t nr_regions ; /* total number of present regions */
uint64_t max_region ; /* size of the regions table */
uint64_t interval_ns ; /* sampling interval in nanoseconds */
2015-08-20 13:55:06 +03:00
uint64_t timescale ; /* default sample value multiplier */
int precise ; /* use precise_timestamps when creating regions */
2015-08-05 12:40:00 +03:00
struct dm_stats_region * regions ;
/* statistics cursor */
uint64_t cur_region ;
uint64_t cur_area ;
} ;
# define PROC_SELF_COMM " / proc / self / comm"
static char * _program_id_from_proc ( void )
{
FILE * comm = NULL ;
char buf [ 256 ] ;
if ( ! ( comm = fopen ( PROC_SELF_COMM , " r " ) ) )
return_NULL ;
2015-08-10 12:08:03 +03:00
if ( ! fgets ( buf , sizeof ( buf ) , comm ) ) {
log_error ( " Could not read from %s " , PROC_SELF_COMM ) ;
2015-08-10 21:23:41 +03:00
if ( fclose ( comm ) )
stack ;
2015-08-10 12:08:03 +03:00
return NULL ;
}
2015-08-05 12:40:00 +03:00
2015-08-10 21:23:41 +03:00
if ( fclose ( comm ) )
stack ;
2015-08-05 12:40:00 +03:00
return dm_strdup ( buf ) ;
}
2015-08-19 22:39:10 +03:00
static uint64_t _nr_areas ( uint64_t len , uint64_t step )
{
/* Default is one area. */
if ( ! len | | ! step )
return 1 ;
/*
* drivers / md / dm - stats . c : : message_stats_create ( )
* A region may be sub - divided into areas with their own counters .
* Any partial area at the end of the region is treated as an
* additional complete area .
*/
return ( len + step - 1 ) / step ;
}
static uint64_t _nr_areas_region ( struct dm_stats_region * region )
{
return _nr_areas ( region - > len , region - > step ) ;
}
2015-08-05 12:40:00 +03:00
struct dm_stats * dm_stats_create ( const char * program_id )
{
2015-08-19 22:39:10 +03:00
size_t hist_hint = sizeof ( struct dm_histogram_bin ) ;
2015-08-05 12:40:00 +03:00
struct dm_stats * dms = NULL ;
2015-08-10 12:15:22 +03:00
if ( ! ( dms = dm_zalloc ( sizeof ( * dms ) ) ) )
2015-08-05 12:40:00 +03:00
return_NULL ;
2015-08-19 22:39:10 +03:00
/* FIXME: better hint. */
2015-08-05 12:40:00 +03:00
if ( ! ( dms - > mem = dm_pool_create ( " stats_pool " , 4096 ) ) )
2015-08-10 12:15:22 +03:00
goto_out ;
2015-08-05 12:40:00 +03:00
2015-08-19 22:39:10 +03:00
if ( ! ( dms - > hist_mem = dm_pool_create ( " histogram_pool " , hist_hint ) ) )
return_0 ;
2015-08-05 12:40:00 +03:00
if ( ! program_id | | ! strlen ( program_id ) )
dms - > program_id = _program_id_from_proc ( ) ;
else
dms - > program_id = dm_strdup ( program_id ) ;
dms - > major = - 1 ;
dms - > minor = - 1 ;
dms - > name = NULL ;
dms - > uuid = NULL ;
2015-08-20 13:55:06 +03:00
/* by default all regions use msec precision */
2015-08-05 12:40:00 +03:00
dms - > timescale = NSEC_PER_MSEC ;
2015-08-20 13:55:06 +03:00
dms - > precise = 0 ;
2015-08-05 12:40:00 +03:00
dms - > nr_regions = DM_STATS_REGION_NOT_PRESENT ;
dms - > max_region = DM_STATS_REGION_NOT_PRESENT ;
dms - > regions = NULL ;
return dms ;
2015-08-10 12:15:22 +03:00
out :
dm_free ( dms ) ;
return NULL ;
2015-08-05 12:40:00 +03:00
}
/**
* Test whether the stats region pointed to by region is present .
*/
static int _stats_region_present ( const struct dm_stats_region * region )
{
return ! ( region - > region_id = = DM_STATS_REGION_NOT_PRESENT ) ;
}
2015-08-19 22:39:10 +03:00
static void _stats_histograms_destroy ( struct dm_pool * mem ,
struct dm_stats_region * region )
{
uint64_t n ;
/* Unpopulated handle. */
if ( ! region - > counters )
return ;
for ( n = _nr_areas_region ( region ) - 1 ; n ; n - - )
if ( region - > counters [ n ] . histogram )
dm_pool_free ( mem , region - > counters [ n ] . histogram ) ;
}
2015-08-05 12:40:00 +03:00
static void _stats_region_destroy ( struct dm_stats_region * region )
{
if ( ! _stats_region_present ( region ) )
return ;
/**
* Don ' t free counters here explicitly ; it will be dropped
* from the pool along with the corresponding regions table .
2015-08-19 22:39:10 +03:00
*
* The following objects are all allocated with dm_malloc .
2015-08-05 12:40:00 +03:00
*/
if ( region - > program_id )
dm_free ( region - > program_id ) ;
if ( region - > aux_data )
dm_free ( region - > aux_data ) ;
}
static void _stats_regions_destroy ( struct dm_stats * dms )
{
struct dm_pool * mem = dms - > mem ;
uint64_t i ;
if ( ! dms - > regions )
return ;
/* walk backwards to obey pool order */
2015-08-19 22:39:10 +03:00
for ( i = dms - > max_region ; ( i ! = DM_STATS_REGION_NOT_PRESENT ) ; i - - ) {
_stats_histograms_destroy ( dms - > hist_mem , & dms - > regions [ i ] ) ;
2015-08-05 12:40:00 +03:00
_stats_region_destroy ( & dms - > regions [ i ] ) ;
2015-08-19 22:39:10 +03:00
}
2015-08-05 12:40:00 +03:00
dm_pool_free ( mem , dms - > regions ) ;
}
static int _set_stats_device ( struct dm_stats * dms , struct dm_task * dmt )
{
if ( dms - > name )
return dm_task_set_name ( dmt , dms - > name ) ;
if ( dms - > uuid )
return dm_task_set_uuid ( dmt , dms - > uuid ) ;
if ( dms - > major > 0 )
return dm_task_set_major ( dmt , dms - > major )
& & dm_task_set_minor ( dmt , dms - > minor ) ;
return_0 ;
}
static int _stats_bound ( struct dm_stats * dms )
{
if ( dms - > major > 0 | | dms - > name | | dms - > uuid )
return 1 ;
/* %p format specifier expects a void pointer. */
log_debug ( " Stats handle at %p is not bound. " , ( void * ) dms ) ;
return 0 ;
}
static void _stats_clear_binding ( struct dm_stats * dms )
{
if ( dms - > name )
dm_pool_free ( dms - > mem , dms - > name ) ;
if ( dms - > uuid )
dm_pool_free ( dms - > mem , dms - > uuid ) ;
dms - > name = dms - > uuid = NULL ;
dms - > major = dms - > minor = - 1 ;
}
int dm_stats_bind_devno ( struct dm_stats * dms , int major , int minor )
{
_stats_clear_binding ( dms ) ;
_stats_regions_destroy ( dms ) ;
dms - > major = major ;
dms - > minor = minor ;
return 1 ;
}
int dm_stats_bind_name ( struct dm_stats * dms , const char * name )
{
_stats_clear_binding ( dms ) ;
_stats_regions_destroy ( dms ) ;
if ( ! ( dms - > name = dm_pool_strdup ( dms - > mem , name ) ) )
return_0 ;
return 1 ;
}
int dm_stats_bind_uuid ( struct dm_stats * dms , const char * uuid )
{
_stats_clear_binding ( dms ) ;
_stats_regions_destroy ( dms ) ;
if ( ! ( dms - > uuid = dm_pool_strdup ( dms - > mem , uuid ) ) )
return_0 ;
return 1 ;
}
2015-08-20 13:55:06 +03:00
static int _stats_check_precise_timestamps ( const struct dm_stats * dms )
{
/* Already checked? */
if ( dms & & dms - > precise )
return 1 ;
2015-08-19 22:39:10 +03:00
2015-08-20 13:55:06 +03:00
return dm_message_supports_precise_timestamps ( ) ;
}
int dm_stats_driver_supports_precise ( void )
{
return _stats_check_precise_timestamps ( NULL ) ;
}
2015-08-19 22:39:10 +03:00
int dm_stats_driver_supports_histogram ( void )
{
return _stats_check_precise_timestamps ( NULL ) ;
}
static char * _build_histogram_arg ( struct dm_histogram * bounds , int * precise )
{
struct dm_histogram_bin * entry , * bins ;
size_t hist_len = 1 , len = 0 ;
char * hist_arg , * arg = NULL ;
uint64_t scale ;
entry = bins = bounds - > bins ;
/* Empty histogram is invalid. */
if ( ! bounds - > nr_bins ) {
log_error ( " Cannot format empty histogram description. " ) ;
return NULL ;
}
entry + = bounds - > nr_bins - 1 ;
while ( entry > = bins ) {
double value ;
if ( entry ! = bins ) {
if ( entry - > upper < ( entry - 1 ) - > upper ) {
log_error ( " Histogram boundaries must be in "
" order of increasing magnitude. " ) ;
return 0 ;
}
hist_len + + ; /* ',' */
}
/*
* Only enable precise_timestamps automatically if any
* value in the histogram bounds uses precision < 1 ms .
*/
if ( ! * precise & & ( entry - > upper % NSEC_PER_MSEC ) )
* precise = 1 ;
value = ( double ) ( entry - - ) - > upper ;
/* Use lround to avoid size_t -> double cast warning. */
hist_len + = 1 + ( size_t ) lround ( log10 ( value ) ) ;
}
if ( ! ( hist_arg = dm_zalloc ( hist_len ) ) ) {
log_error ( " Could not allocate memory for histogram argument. " ) ;
return 0 ;
}
arg = hist_arg ;
if ( * precise )
scale = 1 ;
else
scale = ( * precise ) ? 1 : NSEC_PER_MSEC ;
for ( entry = bins ; entry < ( bins + bounds - > nr_bins ) ; entry + + ) {
uint64_t value ;
ssize_t l = 0 ;
int last = ! ( entry < ( bins + bounds - > nr_bins - 1 ) ) ;
value = entry - > upper / scale ;
if ( ( l = dm_snprintf ( arg , hist_len - len , FMTu64 " %s " , value ,
( last ) ? " " : " , " ) ) < 0 )
goto out ;
len + = ( size_t ) l ;
arg + = ( size_t ) l ;
}
return hist_arg ;
out :
log_error ( " Could not build histogram arguments. " ) ;
dm_free ( hist_arg ) ;
return NULL ;
}
2015-08-05 12:40:00 +03:00
static struct dm_task * _stats_send_message ( struct dm_stats * dms , char * msg )
{
struct dm_task * dmt ;
if ( ! ( dmt = dm_task_create ( DM_DEVICE_TARGET_MSG ) ) )
return_0 ;
if ( ! _set_stats_device ( dms , dmt ) )
goto_out ;
if ( ! dm_task_set_message ( dmt , msg ) )
goto_out ;
if ( ! dm_task_run ( dmt ) )
goto_out ;
return dmt ;
out :
dm_task_destroy ( dmt ) ;
return NULL ;
}
2015-08-19 22:39:10 +03:00
/*
* Parse a histogram specification returned by the kernel in a
* @ stats_list response .
*/
static int _stats_parse_histogram_spec ( struct dm_stats * dms ,
struct dm_stats_region * region ,
const char * histogram )
{
static const char * _valid_chars = " 0123456789, " ;
uint64_t scale = region - > timescale ;
struct dm_pool * mem = dms - > hist_mem ;
struct dm_histogram_bin cur ;
struct dm_histogram hist ;
unsigned nr_bins = 1 ;
const char * c , * v ;
char * p ;
/* Advance past "histogram:". */
histogram = strchr ( histogram , ' : ' ) ;
if ( ! histogram ) {
log_error ( " Could not parse histogram description. " ) ;
return 0 ;
}
histogram + + ;
/* @stats_list rows are newline terminated. */
if ( ( p = strchr ( histogram , ' \n ' ) ) )
* p = ' \0 ' ;
if ( ! dm_pool_begin_object ( mem , sizeof ( cur ) ) )
return_0 ;
memset ( & hist , 0 , sizeof ( hist ) ) ;
hist . nr_bins = 0 ; /* fix later */
hist . region = region ;
hist . dms = dms ;
if ( ! dm_pool_grow_object ( mem , & hist , sizeof ( hist ) ) )
goto_out ;
c = histogram ;
do {
for ( v = _valid_chars ; * v ; v + + )
if ( * c = = * v )
break ;
if ( ! * v )
goto badchar ;
if ( * c = = ' , ' ) {
log_error ( " Invalid histogram description: %s " ,
histogram ) ;
goto out ;
} else {
const char * val_start = c ;
char * endptr = NULL ;
uint64_t this_val = 0 ;
this_val = strtoull ( val_start , & endptr , 10 ) ;
if ( ! endptr ) {
log_error ( " Could not parse histogram boundary. " ) ;
goto out ;
}
c = endptr ; /* Advance to units, comma, or end. */
if ( * c = = ' , ' )
c + + ;
else if ( * c | | ( * c = = ' ' ) ) /* Expected ',' or NULL. */
goto badchar ;
if ( * c = = ' , ' )
c + + ;
cur . upper = scale * this_val ;
cur . count = 0 ;
if ( ! dm_pool_grow_object ( mem , & cur , sizeof ( cur ) ) )
goto_out ;
nr_bins + + ;
}
} while ( * c & & ( * c ! = ' ' ) ) ;
/* final upper bound. */
cur . upper = UINT64_MAX ;
if ( ! dm_pool_grow_object ( mem , & cur , sizeof ( cur ) ) )
goto_out ;
region - > bounds = dm_pool_end_object ( mem ) ;
if ( ! region - > bounds )
return_0 ;
region - > bounds - > nr_bins = nr_bins ;
log_debug ( " Added region histogram spec with %d entries. " , nr_bins ) ;
return 1 ;
badchar :
log_error ( " Invalid character in histogram: '%c' (0x%x) " , * c , * c ) ;
out :
dm_pool_abandon_object ( mem ) ;
return 0 ;
}
static int _stats_parse_list_region ( struct dm_stats * dms ,
struct dm_stats_region * region , char * line )
2015-08-05 12:40:00 +03:00
{
2015-08-20 13:55:06 +03:00
char * p = NULL , string_data [ 4096 ] ; /* FIXME: add dm_sscanf with %ms? */
const char * program_id , * aux_data , * stats_args ;
const char * empty_string = " " ;
2015-08-05 12:40:00 +03:00
int r ;
2015-08-20 13:55:06 +03:00
memset ( string_data , 0 , sizeof ( string_data ) ) ;
/*
* Parse fixed fields , line format :
*
* < region_id > : < start_sector > + < length > < step > < string data >
*
* Maximum string data size is 4096 - 1 bytes .
2015-08-05 12:40:00 +03:00
*/
2015-08-20 13:55:06 +03:00
r = sscanf ( line , FMTu64 " : " FMTu64 " + " FMTu64 " " FMTu64 " %4095c " ,
& region - > region_id , & region - > start , & region - > len ,
& region - > step , string_data ) ;
2015-08-05 12:40:00 +03:00
2015-08-20 13:55:06 +03:00
if ( r ! = 5 )
2015-08-05 12:40:00 +03:00
return_0 ;
2015-08-20 13:55:06 +03:00
/* program_id is guaranteed to be first. */
program_id = string_data ;
/*
* FIXME : support embedded ' \ ' in string data :
* s / strchr / _find_unescaped_space ( ) /
*/
if ( ( p = strchr ( string_data , ' ' ) ) ) {
/* terminate program_id string. */
* p = ' \0 ' ;
if ( ! strcmp ( program_id , " - " ) )
program_id = empty_string ;
aux_data = p + 1 ;
if ( ( p = strchr ( aux_data , ' ' ) ) ) {
/* terminate aux_data string. */
* p = ' \0 ' ;
if ( ! strcmp ( aux_data , " - " ) )
aux_data = empty_string ;
stats_args = p + 1 ;
} else
stats_args = empty_string ;
} else
aux_data = stats_args = empty_string ;
if ( strstr ( stats_args , PRECISE_ARG ) )
region - > timescale = 1 ;
else
region - > timescale = NSEC_PER_MSEC ;
2015-08-05 12:40:00 +03:00
2015-08-19 22:39:10 +03:00
if ( ( p = strstr ( stats_args , HISTOGRAM_ARG ) ) ) {
if ( ! _stats_parse_histogram_spec ( dms , region , p ) )
return_0 ;
} else
region - > bounds = NULL ;
2015-08-05 12:40:00 +03:00
if ( ! ( region - > program_id = dm_strdup ( program_id ) ) )
return_0 ;
if ( ! ( region - > aux_data = dm_strdup ( aux_data ) ) ) {
dm_free ( region - > program_id ) ;
return_0 ;
}
region - > counters = NULL ;
return 1 ;
}
static int _stats_parse_list ( struct dm_stats * dms , const char * resp )
{
struct dm_pool * mem = dms - > mem ;
struct dm_stats_region cur ;
uint64_t max_region = 0 , nr_regions = 0 ;
FILE * list_rows ;
/* FIXME: determine correct maximum line length based on kernel format */
char line [ 256 ] ;
if ( ! resp ) {
log_error ( " Could not parse NULL @stats_list response. " ) ;
return 0 ;
}
if ( dms - > regions )
_stats_regions_destroy ( dms ) ;
/* no regions */
if ( ! strlen ( resp ) ) {
dms - > nr_regions = dms - > max_region = 0 ;
dms - > regions = NULL ;
return 1 ;
}
/*
* dm_task_get_message_response ( ) returns a ' const char * ' but
* since fmemopen also permits " w " it expects a ' char * ' .
*/
if ( ! ( list_rows = fmemopen ( ( char * ) resp , strlen ( resp ) , " r " ) ) )
return_0 ;
if ( ! dm_pool_begin_object ( mem , 1024 ) )
goto_out ;
while ( fgets ( line , sizeof ( line ) , list_rows ) ) {
2015-08-19 22:39:10 +03:00
if ( ! _stats_parse_list_region ( dms , & cur , line ) )
2015-08-05 12:40:00 +03:00
goto_out ;
/* handle holes in the list of region_ids */
if ( cur . region_id > max_region ) {
struct dm_stats_region fill ;
memset ( & fill , 0 , sizeof ( fill ) ) ;
fill . region_id = DM_STATS_REGION_NOT_PRESENT ;
do {
if ( ! dm_pool_grow_object ( mem , & fill , sizeof ( fill ) ) )
goto_out ;
} while ( max_region + + < ( cur . region_id - 1 ) ) ;
}
if ( ! dm_pool_grow_object ( mem , & cur , sizeof ( cur ) ) )
goto_out ;
max_region + + ;
nr_regions + + ;
}
dms - > nr_regions = nr_regions ;
dms - > max_region = max_region - 1 ;
dms - > regions = dm_pool_end_object ( mem ) ;
2015-08-10 21:23:41 +03:00
if ( fclose ( list_rows ) )
stack ;
2015-08-05 12:40:00 +03:00
return 1 ;
out :
2015-08-10 21:23:41 +03:00
if ( fclose ( list_rows ) )
stack ;
2015-08-05 12:40:00 +03:00
dm_pool_abandon_object ( mem ) ;
return 0 ;
}
int dm_stats_list ( struct dm_stats * dms , const char * program_id )
{
struct dm_task * dmt ;
char msg [ 256 ] ;
int r ;
if ( ! _stats_bound ( dms ) )
return_0 ;
/* allow zero-length program_id for list */
if ( ! program_id )
program_id = dms - > program_id ;
r = dm_snprintf ( msg , sizeof ( msg ) , " @stats_list %s " , program_id ) ;
if ( r < 0 ) {
log_error ( " Failed to prepare stats message. " ) ;
return 0 ;
}
if ( ! ( dmt = _stats_send_message ( dms , msg ) ) )
return 0 ;
if ( ! _stats_parse_list ( dms , dm_task_get_message_response ( dmt ) ) ) {
log_error ( " Could not parse @stats_list response. " ) ;
goto out ;
}
dm_task_destroy ( dmt ) ;
return 1 ;
out :
dm_task_destroy ( dmt ) ;
return 0 ;
}
2015-08-19 22:39:10 +03:00
/*
* Parse histogram data returned from a @ stats_print operation .
*/
static int _stats_parse_histogram ( struct dm_pool * mem , char * hist_str ,
struct dm_histogram * * histogram ,
struct dm_stats_region * region )
{
static const char * _valid_chars = " 0123456789: " ;
int nr_bins = region - > bounds - > nr_bins ;
struct dm_histogram hist , * bounds = region - > bounds ;
struct dm_histogram_bin cur ;
uint64_t sum = 0 ;
const char * c , * v ;
int bin = 0 ;
c = hist_str ;
dm_pool_begin_object ( mem , sizeof ( cur ) ) ;
hist . nr_bins = nr_bins ;
dm_pool_grow_object ( mem , & hist , sizeof ( hist ) ) ;
do {
memset ( & cur , 0 , sizeof ( cur ) ) ;
for ( v = _valid_chars ; * v ; v + + )
if ( * c = = * v )
break ;
if ( ! * v )
goto badchar ;
if ( * c = = ' , ' ) {
log_error ( " Invalid histogram: %s " , hist_str ) ;
goto out ;
} else {
const char * val_start = c ;
char * endptr = NULL ;
uint64_t this_val = 0 ;
this_val = strtoull ( val_start , & endptr , 10 ) ;
if ( ! endptr ) {
log_error ( " Could not parse histogram value. " ) ;
goto out ;
}
c = endptr ; /* Advance to colon, or end. */
if ( * c = = ' : ' )
c + + ;
else if ( * c & ( * c ! = ' \n ' ) )
/* Expected ':', '\n', or NULL. */
goto badchar ;
if ( * c = = ' : ' )
c + + ;
cur . upper = bounds - > bins [ bin ] . upper ;
cur . count = this_val ;
sum + = this_val ;
dm_pool_grow_object ( mem , & cur , sizeof ( cur ) ) ;
bin + + ;
}
} while ( * c & & ( * c ! = ' \n ' ) ) ;
log_debug ( " Added region histogram data with %d entries. " , nr_bins ) ;
* histogram = dm_pool_end_object ( mem ) ;
( * histogram ) - > sum = sum ;
return 1 ;
badchar :
log_error ( " Invalid character in histogram data: '%c' (0x%x) " , * c , * c ) ;
out :
return 0 ;
}
static int _stats_parse_region ( struct dm_stats * dms , const char * resp ,
2015-08-05 12:40:00 +03:00
struct dm_stats_region * region ,
uint64_t timescale )
{
2015-08-19 22:39:10 +03:00
struct dm_histogram * hist = NULL ;
2015-08-05 12:40:00 +03:00
struct dm_stats_counters cur ;
2015-08-19 22:39:10 +03:00
struct dm_pool * mem = dms - > mem ;
2015-08-05 12:40:00 +03:00
FILE * stats_rows = NULL ;
uint64_t start , len ;
char row [ 256 ] ;
int r ;
if ( ! resp ) {
log_error ( " Could not parse empty @stats_print response. " ) ;
return 0 ;
}
region - > start = UINT64_MAX ;
if ( ! dm_pool_begin_object ( mem , 512 ) )
goto_out ;
/*
* dm_task_get_message_response ( ) returns a ' const char * ' but
* since fmemopen also permits " w " it expects a ' char * ' .
*/
stats_rows = fmemopen ( ( char * ) resp , strlen ( resp ) , " r " ) ;
if ( ! stats_rows )
goto_out ;
/*
* Output format for each step - sized area of a region :
*
* < start_sector > + < length > counters
*
* The first 11 counters have the same meaning as
* / sys / block / * / stat or / proc / diskstats .
*
* Please refer to Documentation / iostats . txt for details .
*
* 1. the number of reads completed
* 2. the number of reads merged
* 3. the number of sectors read
* 4. the number of milliseconds spent reading
* 5. the number of writes completed
* 6. the number of writes merged
* 7. the number of sectors written
* 8. the number of milliseconds spent writing
* 9. the number of I / Os currently in progress
* 10. the number of milliseconds spent doing I / Os
* 11. the weighted number of milliseconds spent doing I / Os
*
* Additional counters :
* 12. the total time spent reading in milliseconds
* 13. the total time spent writing in milliseconds
*
*/
while ( fgets ( row , sizeof ( row ) , stats_rows ) ) {
r = sscanf ( row , FMTu64 " + " FMTu64 /* start+len */
/* reads */
FMTu64 " " FMTu64 " " FMTu64 " " FMTu64 " "
/* writes */
FMTu64 " " FMTu64 " " FMTu64 " " FMTu64 " "
/* in flight & io nsecs */
FMTu64 " " FMTu64 " " FMTu64 " "
/* tot read/write nsecs */
FMTu64 " " FMTu64 , & start , & len ,
& cur . reads , & cur . reads_merged , & cur . read_sectors ,
& cur . read_nsecs ,
& cur . writes , & cur . writes_merged , & cur . write_sectors ,
& cur . write_nsecs ,
& cur . io_in_progress ,
& cur . io_nsecs , & cur . weighted_io_nsecs ,
& cur . total_read_nsecs , & cur . total_write_nsecs ) ;
if ( r ! = 15 ) {
log_error ( " Could not parse @stats_print row. " ) ;
goto out ;
}
/* scale time values up if needed */
if ( timescale ! = 1 ) {
cur . read_nsecs * = timescale ;
cur . write_nsecs * = timescale ;
cur . io_nsecs * = timescale ;
cur . weighted_io_nsecs * = timescale ;
cur . total_read_nsecs * = timescale ;
cur . total_write_nsecs * = timescale ;
}
2015-08-19 22:39:10 +03:00
if ( region - > bounds ) {
/* Find first histogram separator. */
char * hist_str = strchr ( row , ' : ' ) ;
if ( ! hist_str ) {
log_error ( " Could not parse histogram value. " ) ;
goto out ;
}
/* Find space preceding histogram. */
while ( hist_str & & * ( hist_str - 1 ) ! = ' ' )
hist_str - - ;
/* Use a separate pool for histogram objects since we
* are growing the area table and each area ' s histogram
* table simultaneously .
*/
if ( ! _stats_parse_histogram ( dms - > hist_mem , hist_str ,
& hist , region ) )
goto out ;
hist - > dms = dms ;
hist - > region = region ;
}
cur . histogram = hist ;
2015-08-05 12:40:00 +03:00
if ( ! dm_pool_grow_object ( mem , & cur , sizeof ( cur ) ) )
goto_out ;
2015-08-19 22:39:10 +03:00
2015-08-05 12:40:00 +03:00
if ( region - > start = = UINT64_MAX ) {
region - > start = start ;
region - > step = len ; /* area size is always uniform. */
}
}
region - > len = ( start + len ) - region - > start ;
region - > timescale = timescale ;
region - > counters = dm_pool_end_object ( mem ) ;
2015-08-10 21:23:41 +03:00
if ( fclose ( stats_rows ) )
stack ;
2015-08-05 12:40:00 +03:00
return 1 ;
out :
if ( stats_rows )
2015-08-10 21:23:41 +03:00
if ( fclose ( stats_rows ) )
stack ;
2015-08-05 12:40:00 +03:00
dm_pool_abandon_object ( mem ) ;
return 0 ;
}
static void _stats_walk_next ( const struct dm_stats * dms , int region ,
uint64_t * cur_r , uint64_t * cur_a )
{
struct dm_stats_region * cur = NULL ;
int present ;
if ( ! dms | | ! dms - > regions )
return ;
cur = & dms - > regions [ * cur_r ] ;
present = _stats_region_present ( cur ) ;
if ( region & & present )
* cur_a = _nr_areas_region ( cur ) ;
if ( region | | ! present | | + + ( * cur_a ) = = _nr_areas_region ( cur ) ) {
* cur_a = 0 ;
while ( ! dm_stats_region_present ( dms , + + ( * cur_r ) )
& & * cur_r < dms - > max_region )
; /* keep walking until a present region is found
* or the end of the table is reached . */
}
}
static void _stats_walk_start ( const struct dm_stats * dms ,
uint64_t * cur_r , uint64_t * cur_a )
{
if ( ! dms | | ! dms - > regions )
return ;
* cur_r = 0 ;
* cur_a = 0 ;
/* advance to the first present region */
if ( ! dm_stats_region_present ( dms , dms - > cur_region ) )
_stats_walk_next ( dms , 0 , cur_r , cur_a ) ;
}
void dm_stats_walk_start ( struct dm_stats * dms )
{
_stats_walk_start ( dms , & dms - > cur_region , & dms - > cur_area ) ;
}
void dm_stats_walk_next ( struct dm_stats * dms )
{
_stats_walk_next ( dms , 0 , & dms - > cur_region , & dms - > cur_area ) ;
}
void dm_stats_walk_next_region ( struct dm_stats * dms )
{
_stats_walk_next ( dms , 1 , & dms - > cur_region , & dms - > cur_area ) ;
}
static int _stats_walk_end ( const struct dm_stats * dms ,
uint64_t * cur_r , uint64_t * cur_a )
{
struct dm_stats_region * region = NULL ;
int end = 0 ;
if ( ! dms | | ! dms - > regions )
return 1 ;
region = & dms - > regions [ * cur_r ] ;
end = ( * cur_r > dms - > max_region
| | ( * cur_r = = dms - > max_region
& & * cur_a > = _nr_areas_region ( region ) ) ) ;
return end ;
}
int dm_stats_walk_end ( struct dm_stats * dms )
{
return _stats_walk_end ( dms , & dms - > cur_region , & dms - > cur_area ) ;
}
uint64_t dm_stats_get_region_nr_areas ( const struct dm_stats * dms ,
uint64_t region_id )
{
struct dm_stats_region * region = & dms - > regions [ region_id ] ;
return _nr_areas_region ( region ) ;
}
uint64_t dm_stats_get_current_nr_areas ( const struct dm_stats * dms )
{
return dm_stats_get_region_nr_areas ( dms , dms - > cur_region ) ;
}
uint64_t dm_stats_get_nr_areas ( const struct dm_stats * dms )
{
uint64_t nr_areas = 0 ;
/* use a separate cursor */
uint64_t cur_region , cur_area ;
_stats_walk_start ( dms , & cur_region , & cur_area ) ;
do {
nr_areas + = dm_stats_get_current_nr_areas ( dms ) ;
_stats_walk_next ( dms , 1 , & cur_region , & cur_area ) ;
} while ( ! _stats_walk_end ( dms , & cur_region , & cur_area ) ) ;
return nr_areas ;
}
2015-08-19 22:39:10 +03:00
int dm_stats_get_region_nr_histogram_bins ( const struct dm_stats * dms ,
uint64_t region_id )
{
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
if ( ! dms - > regions [ region_id ] . bounds )
return 0 ;
return dms - > regions [ region_id ] . bounds - > nr_bins ;
}
2015-08-20 13:55:06 +03:00
static int _stats_create_region ( struct dm_stats * dms , uint64_t * region_id ,
uint64_t start , uint64_t len , int64_t step ,
2015-08-19 22:39:10 +03:00
int precise , const char * hist_arg ,
2015-08-20 13:55:06 +03:00
const char * program_id , const char * aux_data )
2015-08-05 12:40:00 +03:00
{
struct dm_task * dmt = NULL ;
char msg [ 1024 ] , range [ 64 ] ;
const char * err_fmt = " Could not prepare @stats_create %s. " ;
2015-08-20 13:55:06 +03:00
const char * precise_str = PRECISE_ARG ;
const char * resp , * opt_args = NULL ;
2015-08-19 22:39:10 +03:00
int r = 0 , nr_opt = 0 ; /* number of optional args. */
2015-08-05 12:40:00 +03:00
if ( ! _stats_bound ( dms ) )
return_0 ;
if ( ! program_id | | ! strlen ( program_id ) )
program_id = dms - > program_id ;
if ( start | | len ) {
if ( ! dm_snprintf ( range , sizeof ( range ) , FMTu64 " + " FMTu64 ,
start , len ) ) {
log_error ( err_fmt , " range " ) ;
2015-08-20 13:55:06 +03:00
return 0 ;
2015-08-05 12:40:00 +03:00
}
}
2015-08-20 13:55:06 +03:00
if ( precise < 0 )
precise = dms - > precise ;
if ( precise )
nr_opt + + ;
else
precise_str = " " ;
2015-08-19 22:39:10 +03:00
if ( hist_arg )
nr_opt + + ;
else
hist_arg = " " ;
2015-08-20 13:55:06 +03:00
if ( nr_opt ) {
2015-08-19 22:39:10 +03:00
if ( ( dm_asprintf ( ( char * * ) & opt_args , " %d %s %s%s " , nr_opt ,
precise_str ,
( strlen ( hist_arg ) ) ? HISTOGRAM_ARG : " " ,
hist_arg ) ) < 0 ) {
2015-08-20 13:55:06 +03:00
log_error ( err_fmt , PRECISE_ARG " option. " ) ;
return 0 ;
}
} else
opt_args = dm_strdup ( " " ) ;
if ( ! dm_snprintf ( msg , sizeof ( msg ) , " @stats_create %s %s " FMTu64
" %s %s %s " , ( start | | len ) ? range : " - " ,
2015-08-05 12:40:00 +03:00
( step < 0 ) ? " / " : " " ,
2015-08-20 13:55:06 +03:00
( uint64_t ) llabs ( step ) ,
opt_args , program_id , aux_data ) ) {
2015-08-05 12:40:00 +03:00
log_error ( err_fmt , " message " ) ;
2015-08-20 13:55:06 +03:00
dm_free ( ( void * ) opt_args ) ;
return 0 ;
2015-08-05 12:40:00 +03:00
}
if ( ! ( dmt = _stats_send_message ( dms , msg ) ) )
goto out ;
resp = dm_task_get_message_response ( dmt ) ;
if ( ! resp ) {
log_error ( " Could not parse empty @stats_create response. " ) ;
goto out ;
}
if ( region_id ) {
char * endptr = NULL ;
* region_id = strtoull ( resp , & endptr , 10 ) ;
if ( resp = = endptr )
goto_out ;
}
2015-08-20 13:55:06 +03:00
r = 1 ;
2015-08-05 12:40:00 +03:00
out :
if ( dmt )
dm_task_destroy ( dmt ) ;
2015-08-20 13:55:06 +03:00
dm_free ( ( void * ) opt_args ) ;
return r ;
}
int dm_stats_create_region ( struct dm_stats * dms , uint64_t * region_id ,
uint64_t start , uint64_t len , int64_t step ,
2015-08-19 22:39:10 +03:00
int precise , struct dm_histogram * bounds ,
const char * program_id , const char * aux_data )
2015-08-20 13:55:06 +03:00
{
2015-08-19 22:39:10 +03:00
char * hist_arg = NULL ;
int r = 0 ;
/* Nanosecond counters and histograms both need precise_timestamps. */
if ( ( precise | | bounds )
& & ! _stats_check_precise_timestamps ( dms ) )
2015-08-20 13:55:06 +03:00
return 0 ;
2015-08-19 22:39:10 +03:00
if ( bounds ) {
/* _build_histogram_arg enables precise if vals < 1ms. */
if ( ! ( hist_arg = _build_histogram_arg ( bounds , & precise ) ) )
goto_out ;
}
2015-08-20 13:55:06 +03:00
2015-08-19 22:39:10 +03:00
r = _stats_create_region ( dms , region_id , start , len , step ,
precise , hist_arg , program_id , aux_data ) ;
dm_free ( hist_arg ) ;
out :
return r ;
2015-08-05 12:40:00 +03:00
}
int dm_stats_delete_region ( struct dm_stats * dms , uint64_t region_id )
{
struct dm_task * dmt ;
char msg [ 1024 ] ;
if ( ! _stats_bound ( dms ) )
return_0 ;
if ( ! dm_snprintf ( msg , sizeof ( msg ) , " @stats_delete " FMTu64 , region_id ) ) {
log_error ( " Could not prepare @stats_delete message. " ) ;
goto out ;
}
dmt = _stats_send_message ( dms , msg ) ;
if ( ! dmt )
goto_out ;
dm_task_destroy ( dmt ) ;
return 1 ;
out :
return 0 ;
}
int dm_stats_clear_region ( struct dm_stats * dms , uint64_t region_id )
{
struct dm_task * dmt ;
char msg [ 1024 ] ;
if ( ! _stats_bound ( dms ) )
return_0 ;
if ( ! dm_snprintf ( msg , sizeof ( msg ) , " @stats_clear " FMTu64 , region_id ) ) {
log_error ( " Could not prepare @stats_clear message. " ) ;
goto out ;
}
dmt = _stats_send_message ( dms , msg ) ;
if ( ! dmt )
goto_out ;
dm_task_destroy ( dmt ) ;
return 1 ;
out :
return 0 ;
}
static struct dm_task * _stats_print_region ( struct dm_stats * dms ,
uint64_t region_id , unsigned start_line ,
unsigned num_lines , unsigned clear )
{
struct dm_task * dmt = NULL ;
/* @stats_print[_clear] <region_id> [<start_line> <num_lines>] */
const char * clear_str = " _clear " , * lines_fmt = " %u %u " ;
const char * msg_fmt = " @stats_print%s " FMTu64 " %s " ;
const char * err_fmt = " Could not prepare @stats_print %s. " ;
char msg [ 1024 ] , lines [ 64 ] ;
if ( start_line | | num_lines )
if ( ! dm_snprintf ( lines , sizeof ( lines ) ,
lines_fmt , start_line , num_lines ) ) {
log_error ( err_fmt , " row specification " ) ;
goto out ;
}
if ( ! dm_snprintf ( msg , sizeof ( msg ) , msg_fmt , ( clear ) ? clear_str : " " ,
region_id , ( start_line | | num_lines ) ? lines : " " ) ) {
log_error ( err_fmt , " message " ) ;
goto out ;
}
if ( ! ( dmt = _stats_send_message ( dms , msg ) ) )
goto out ;
return dmt ;
out :
return NULL ;
}
char * dm_stats_print_region ( struct dm_stats * dms , uint64_t region_id ,
unsigned start_line , unsigned num_lines ,
unsigned clear )
{
char * resp = NULL ;
struct dm_task * dmt = NULL ;
if ( ! _stats_bound ( dms ) )
return_0 ;
dmt = _stats_print_region ( dms , region_id ,
start_line , num_lines , clear ) ;
if ( ! dmt )
return 0 ;
resp = dm_pool_strdup ( dms - > mem , dm_task_get_message_response ( dmt ) ) ;
dm_task_destroy ( dmt ) ;
if ( ! resp )
log_error ( " Could not allocate memory for response buffer. " ) ;
return resp ;
}
void dm_stats_buffer_destroy ( struct dm_stats * dms , char * buffer )
{
dm_pool_free ( dms - > mem , buffer ) ;
}
uint64_t dm_stats_get_nr_regions ( const struct dm_stats * dms )
{
if ( ! dms | | ! dms - > regions )
return 0 ;
return dms - > nr_regions ;
}
/**
* Test whether region_id is present in this set of stats data
*/
int dm_stats_region_present ( const struct dm_stats * dms , uint64_t region_id )
{
if ( ! dms - > regions )
return 0 ;
if ( region_id > dms - > max_region )
return 0 ;
return _stats_region_present ( & dms - > regions [ region_id ] ) ;
}
static int _dm_stats_populate_region ( struct dm_stats * dms , uint64_t region_id ,
const char * resp )
{
struct dm_stats_region * region = & dms - > regions [ region_id ] ;
if ( ! _stats_bound ( dms ) )
return_0 ;
2015-08-19 22:39:10 +03:00
if ( ! _stats_parse_region ( dms , resp , region , region - > timescale ) ) {
2015-08-05 12:40:00 +03:00
log_error ( " Could not parse @stats_print message response. " ) ;
return 0 ;
}
region - > region_id = region_id ;
return 1 ;
}
int dm_stats_populate ( struct dm_stats * dms , const char * program_id ,
uint64_t region_id )
{
int all_regions = ( region_id = = DM_STATS_REGIONS_ALL ) ;
if ( ! _stats_bound ( dms ) )
return_0 ;
/* allow zero-length program_id for populate */
if ( ! program_id )
program_id = dms - > program_id ;
if ( all_regions & & ! dm_stats_list ( dms , program_id ) ) {
log_error ( " Could not parse @stats_list response. " ) ;
goto out ;
}
/* successful list but no regions registered */
if ( ! dms - > nr_regions )
return 0 ;
dm_stats_walk_start ( dms ) ;
do {
struct dm_task * dmt = NULL ; /* @stats_print task */
const char * resp ;
region_id = ( all_regions )
? dm_stats_get_current_region ( dms ) : region_id ;
/* obtain all lines and clear counter values */
if ( ! ( dmt = _stats_print_region ( dms , region_id , 0 , 0 , 1 ) ) )
goto_out ;
resp = dm_task_get_message_response ( dmt ) ;
if ( ! _dm_stats_populate_region ( dms , region_id , resp ) ) {
dm_task_destroy ( dmt ) ;
goto_out ;
}
dm_task_destroy ( dmt ) ;
dm_stats_walk_next_region ( dms ) ;
} while ( all_regions & & ! dm_stats_walk_end ( dms ) ) ;
return 1 ;
out :
_stats_regions_destroy ( dms ) ;
dms - > regions = NULL ;
return 0 ;
}
/**
* destroy a dm_stats object and all associated regions and counter sets .
*/
void dm_stats_destroy ( struct dm_stats * dms )
{
_stats_regions_destroy ( dms ) ;
_stats_clear_binding ( dms ) ;
dm_pool_destroy ( dms - > mem ) ;
2015-08-19 22:39:10 +03:00
dm_pool_destroy ( dms - > hist_mem ) ;
2015-08-05 12:40:00 +03:00
dm_free ( dms - > program_id ) ;
dm_free ( dms ) ;
}
/**
* Methods for accessing counter fields . All methods share the
* following naming scheme and prototype :
*
* uint64_t dm_stats_get_COUNTER ( struct dm_stats * , uint64_t , uint64_t )
*
* Where the two integer arguments are the region_id and area_id
* respectively .
*/
# define MK_STATS_GET_COUNTER_FN(counter) \
uint64_t dm_stats_get_ # # counter ( const struct dm_stats * dms , \
uint64_t region_id , uint64_t area_id ) \
{ \
region_id = ( region_id = = DM_STATS_REGION_CURRENT ) \
? dms - > cur_region : region_id ; \
area_id = ( area_id = = DM_STATS_REGION_CURRENT ) \
? dms - > cur_area : area_id ; \
return dms - > regions [ region_id ] . counters [ area_id ] . counter ; \
}
MK_STATS_GET_COUNTER_FN ( reads )
MK_STATS_GET_COUNTER_FN ( reads_merged )
MK_STATS_GET_COUNTER_FN ( read_sectors )
MK_STATS_GET_COUNTER_FN ( read_nsecs )
MK_STATS_GET_COUNTER_FN ( writes )
MK_STATS_GET_COUNTER_FN ( writes_merged )
MK_STATS_GET_COUNTER_FN ( write_sectors )
MK_STATS_GET_COUNTER_FN ( write_nsecs )
MK_STATS_GET_COUNTER_FN ( io_in_progress )
MK_STATS_GET_COUNTER_FN ( io_nsecs )
MK_STATS_GET_COUNTER_FN ( weighted_io_nsecs )
MK_STATS_GET_COUNTER_FN ( total_read_nsecs )
MK_STATS_GET_COUNTER_FN ( total_write_nsecs )
# undef MK_STATS_GET_COUNTER_FN
int dm_stats_get_rd_merges_per_sec ( const struct dm_stats * dms , double * rrqm ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
if ( ! dms - > interval_ns )
return_0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
* rrqm = ( ( double ) c - > reads_merged ) / ( double ) dms - > interval_ns ;
return 1 ;
}
int dm_stats_get_wr_merges_per_sec ( const struct dm_stats * dms , double * wrqm ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
if ( ! dms - > interval_ns )
return_0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
* wrqm = ( ( double ) c - > writes_merged ) / ( double ) dms - > interval_ns ;
return 1 ;
}
int dm_stats_get_reads_per_sec ( const struct dm_stats * dms , double * rd_s ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
if ( ! dms - > interval_ns )
return_0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
* rd_s = ( ( double ) c - > reads * NSEC_PER_SEC ) / ( double ) dms - > interval_ns ;
return 1 ;
}
int dm_stats_get_writes_per_sec ( const struct dm_stats * dms , double * wr_s ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
if ( ! dms - > interval_ns )
return_0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
* wr_s = ( ( double ) c - > writes * ( double ) NSEC_PER_SEC )
/ ( double ) dms - > interval_ns ;
return 1 ;
}
int dm_stats_get_read_sectors_per_sec ( const struct dm_stats * dms , double * rsec_s ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
if ( ! dms - > interval_ns )
return_0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
* rsec_s = ( ( double ) c - > read_sectors * ( double ) NSEC_PER_SEC )
/ ( double ) dms - > interval_ns ;
return 1 ;
}
int dm_stats_get_write_sectors_per_sec ( const struct dm_stats * dms , double * wsec_s ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
if ( ! dms - > interval_ns )
return_0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
* wsec_s = ( ( double ) c - > write_sectors * ( double ) NSEC_PER_SEC )
/ ( double ) dms - > interval_ns ;
return 1 ;
}
int dm_stats_get_average_request_size ( const struct dm_stats * dms , double * arqsz ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
uint64_t nr_ios , nr_sectors ;
if ( ! dms - > interval_ns )
return_0 ;
* arqsz = 0.0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
nr_ios = c - > reads + c - > writes ;
nr_sectors = c - > read_sectors + c - > write_sectors ;
if ( nr_ios )
* arqsz = ( double ) nr_sectors / ( double ) nr_ios ;
return 1 ;
}
int dm_stats_get_average_queue_size ( const struct dm_stats * dms , double * qusz ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
uint64_t io_ticks ;
if ( ! dms - > interval_ns )
return_0 ;
* qusz = 0.0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
io_ticks = c - > weighted_io_nsecs ;
if ( io_ticks )
* qusz = ( double ) io_ticks / ( double ) dms - > interval_ns ;
return 1 ;
}
int dm_stats_get_average_wait_time ( const struct dm_stats * dms , double * await ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
uint64_t io_ticks , nr_ios ;
if ( ! dms - > interval_ns )
return_0 ;
* await = 0.0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
io_ticks = c - > read_nsecs + c - > write_nsecs ;
nr_ios = c - > reads + c - > writes ;
if ( nr_ios )
* await = ( double ) io_ticks / ( double ) nr_ios ;
return 1 ;
}
int dm_stats_get_average_rd_wait_time ( const struct dm_stats * dms ,
double * await , uint64_t region_id ,
uint64_t area_id )
{
struct dm_stats_counters * c ;
uint64_t rd_io_ticks , nr_rd_ios ;
if ( ! dms - > interval_ns )
return_0 ;
* await = 0.0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
rd_io_ticks = c - > read_nsecs ;
nr_rd_ios = c - > reads ;
if ( rd_io_ticks )
* await = ( double ) rd_io_ticks / ( double ) nr_rd_ios ;
return 1 ;
}
int dm_stats_get_average_wr_wait_time ( const struct dm_stats * dms ,
double * await , uint64_t region_id ,
uint64_t area_id )
{
struct dm_stats_counters * c ;
uint64_t wr_io_ticks , nr_wr_ios ;
if ( ! dms - > interval_ns )
return_0 ;
* await = 0.0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
wr_io_ticks = c - > write_nsecs ;
nr_wr_ios = c - > writes ;
if ( wr_io_ticks & & nr_wr_ios )
* await = ( double ) wr_io_ticks / ( double ) nr_wr_ios ;
return 1 ;
}
int dm_stats_get_service_time ( const struct dm_stats * dms , double * svctm ,
uint64_t region_id , uint64_t area_id )
{
dm_percent_t util ;
double tput ;
if ( ! dm_stats_get_throughput ( dms , & tput , region_id , area_id ) )
return 0 ;
if ( ! dm_stats_get_utilization ( dms , & util , region_id , area_id ) )
return 0 ;
/* avoid NAN with zero counter values */
if ( ( uint64_t ) tput = = 0 | | ( uint64_t ) util = = 0 ) {
* svctm = 0.0 ;
return 1 ;
}
* svctm = ( ( double ) NSEC_PER_SEC * dm_percent_to_float ( util ) )
/ ( 100.0 * tput ) ;
return 1 ;
}
int dm_stats_get_throughput ( const struct dm_stats * dms , double * tput ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
if ( ! dms - > interval_ns )
return_0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
* tput = ( ( NSEC_PER_SEC * ( ( double ) c - > reads + ( double ) c - > writes ) )
/ ( double ) ( dms - > interval_ns ) ) ;
return 1 ;
}
int dm_stats_get_utilization ( const struct dm_stats * dms , dm_percent_t * util ,
uint64_t region_id , uint64_t area_id )
{
struct dm_stats_counters * c ;
uint64_t io_nsecs ;
if ( ! dms - > interval_ns )
return_0 ;
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_area : area_id ;
c = & ( dms - > regions [ region_id ] . counters [ area_id ] ) ;
/**
* If io_nsec > interval_ns there is something wrong with the clock
* for the last interval ; do not allow a value > 100 % utilization
* to be passed to a dm_make_percent ( ) call . We expect to see these
* at startup if counters have not been cleared before the first read .
*/
io_nsecs = ( c - > io_nsecs < = dms - > interval_ns ) ? c - > io_nsecs : dms - > interval_ns ;
* util = dm_make_percent ( io_nsecs , dms - > interval_ns ) ;
return 1 ;
}
void dm_stats_set_sampling_interval_ms ( struct dm_stats * dms , uint64_t interval_ms )
{
/* All times use nsecs internally. */
dms - > interval_ns = interval_ms * NSEC_PER_MSEC ;
}
void dm_stats_set_sampling_interval_ns ( struct dm_stats * dms , uint64_t interval_ns )
{
dms - > interval_ns = interval_ns ;
}
uint64_t dm_stats_get_sampling_interval_ms ( const struct dm_stats * dms )
{
/* All times use nsecs internally. */
return ( dms - > interval_ns / NSEC_PER_MSEC ) ;
}
uint64_t dm_stats_get_sampling_interval_ns ( const struct dm_stats * dms )
{
/* All times use nsecs internally. */
return ( dms - > interval_ns ) ;
}
int dm_stats_set_program_id ( struct dm_stats * dms , int allow_empty ,
const char * program_id )
{
if ( ! allow_empty & & ( ! program_id | | ! strlen ( program_id ) ) ) {
log_error ( " Empty program_id not permitted without "
" allow_empty=1 " ) ;
return 0 ;
}
if ( ! program_id )
program_id = " " ;
if ( dms - > program_id )
dm_free ( dms - > program_id ) ;
if ( ! ( dms - > program_id = dm_strdup ( program_id ) ) )
return_0 ;
return 1 ;
}
uint64_t dm_stats_get_current_region ( const struct dm_stats * dms )
{
return dms - > cur_region ;
}
uint64_t dm_stats_get_current_area ( const struct dm_stats * dms )
{
return dms - > cur_area ;
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_region_start ( const struct dm_stats * dms , uint64_t * start ,
2015-08-05 12:40:00 +03:00
uint64_t region_id )
{
if ( ! dms | | ! dms - > regions )
return_0 ;
* start = dms - > regions [ region_id ] . start ;
return 1 ;
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_region_len ( const struct dm_stats * dms , uint64_t * len ,
2015-08-05 12:40:00 +03:00
uint64_t region_id )
{
if ( ! dms | | ! dms - > regions )
return_0 ;
* len = dms - > regions [ region_id ] . len ;
return 1 ;
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_region_area_len ( const struct dm_stats * dms , uint64_t * len ,
uint64_t region_id )
2015-08-05 12:40:00 +03:00
{
if ( ! dms | | ! dms - > regions )
return_0 ;
2015-08-17 17:23:27 +03:00
* len = dms - > regions [ region_id ] . step ;
2015-08-05 12:40:00 +03:00
return 1 ;
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_current_region_start ( const struct dm_stats * dms ,
uint64_t * start )
2015-08-05 12:40:00 +03:00
{
return dm_stats_get_region_start ( dms , start , dms - > cur_region ) ;
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_current_region_len ( const struct dm_stats * dms ,
uint64_t * len )
2015-08-05 12:40:00 +03:00
{
return dm_stats_get_region_len ( dms , len , dms - > cur_region ) ;
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_current_region_area_len ( const struct dm_stats * dms ,
uint64_t * step )
2015-08-05 12:40:00 +03:00
{
return dm_stats_get_region_area_len ( dms , step , dms - > cur_region ) ;
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_area_start ( const struct dm_stats * dms , uint64_t * start ,
uint64_t region_id , uint64_t area_id )
2015-08-05 12:40:00 +03:00
{
2015-08-17 20:25:22 +03:00
struct dm_stats_region * region ;
2015-08-05 12:40:00 +03:00
if ( ! dms | | ! dms - > regions )
return_0 ;
2015-08-17 20:25:22 +03:00
region = & dms - > regions [ region_id ] ;
2015-08-14 22:28:06 +03:00
* start = region - > start + region - > step * area_id ;
2015-08-05 12:40:00 +03:00
return 1 ;
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_area_offset ( const struct dm_stats * dms , uint64_t * offset ,
uint64_t region_id , uint64_t area_id )
2015-08-14 22:30:05 +03:00
{
if ( ! dms | | ! dms - > regions )
return_0 ;
* offset = dms - > regions [ region_id ] . step * area_id ;
2015-08-17 15:56:44 +03:00
return 1 ;
2015-08-14 22:30:05 +03:00
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_current_area_start ( const struct dm_stats * dms ,
uint64_t * start )
2015-08-05 12:40:00 +03:00
{
return dm_stats_get_area_start ( dms , start ,
dms - > cur_region , dms - > cur_area ) ;
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_current_area_offset ( const struct dm_stats * dms ,
2015-08-14 22:30:05 +03:00
uint64_t * offset )
{
return dm_stats_get_area_offset ( dms , offset ,
dms - > cur_region , dms - > cur_area ) ;
}
2015-08-17 17:23:27 +03:00
int dm_stats_get_current_area_len ( const struct dm_stats * dms ,
uint64_t * len )
2015-08-05 12:40:00 +03:00
{
return dm_stats_get_region_area_len ( dms , len , dms - > cur_region ) ;
}
const char * dm_stats_get_region_program_id ( const struct dm_stats * dms ,
uint64_t region_id )
{
const char * program_id = dms - > regions [ region_id ] . program_id ;
return ( program_id ) ? program_id : " " ;
}
const char * dm_stats_get_region_aux_data ( const struct dm_stats * dms ,
uint64_t region_id )
{
const char * aux_data = dms - > regions [ region_id ] . aux_data ;
return ( aux_data ) ? aux_data : " " ;
}
const char * dm_stats_get_current_region_program_id ( const struct dm_stats * dms )
{
return dm_stats_get_region_program_id ( dms , dms - > cur_region ) ;
}
const char * dm_stats_get_current_region_aux_data ( const struct dm_stats * dms )
{
return dm_stats_get_region_aux_data ( dms , dms - > cur_region ) ;
}
2015-08-17 20:08:18 +03:00
int dm_stats_get_region_precise_timestamps ( const struct dm_stats * dms ,
uint64_t region_id )
{
struct dm_stats_region * region = & dms - > regions [ region_id ] ;
return region - > timescale = = 1 ;
}
int dm_stats_get_current_region_precise_timestamps ( const struct dm_stats * dms )
{
return dm_stats_get_region_precise_timestamps ( dms , dms - > cur_region ) ;
}
2015-08-19 22:39:10 +03:00
/*
* Histogram access methods .
*/
struct dm_histogram * dm_stats_get_histogram ( const struct dm_stats * dms ,
uint64_t region_id ,
uint64_t area_id )
{
region_id = ( region_id = = DM_STATS_REGION_CURRENT )
? dms - > cur_region : region_id ;
area_id = ( area_id = = DM_STATS_AREA_CURRENT )
? dms - > cur_area : area_id ;
if ( ! dms - > regions [ region_id ] . counters )
return dms - > regions [ region_id ] . bounds ;
return dms - > regions [ region_id ] . counters [ area_id ] . histogram ;
}
int dm_histogram_get_nr_bins ( const struct dm_histogram * dmh )
{
return dmh - > nr_bins ;
}
uint64_t dm_histogram_get_bin_lower ( const struct dm_histogram * dmh , int bin )
{
return ( ! bin ) ? 0 : dmh - > bins [ bin - 1 ] . upper ;
}
uint64_t dm_histogram_get_bin_upper ( const struct dm_histogram * dmh , int bin )
{
return dmh - > bins [ bin ] . upper ;
}
uint64_t dm_histogram_get_bin_width ( const struct dm_histogram * dmh , int bin )
{
uint64_t upper , lower ;
upper = dm_histogram_get_bin_upper ( dmh , bin ) ;
lower = dm_histogram_get_bin_lower ( dmh , bin ) ;
return ( upper - lower ) ;
}
uint64_t dm_histogram_get_bin_count ( const struct dm_histogram * dmh , int bin )
{
return dmh - > bins [ bin ] . count ;
}
uint64_t dm_histogram_get_sum ( const struct dm_histogram * dmh )
{
return dmh - > sum ;
}
dm_percent_t dm_histogram_get_bin_percent ( const struct dm_histogram * dmh ,
int bin )
{
uint64_t value = dm_histogram_get_bin_count ( dmh , bin ) ;
uint64_t width = dm_histogram_get_bin_width ( dmh , bin ) ;
uint64_t total = dm_histogram_get_sum ( dmh ) ;
double val = ( double ) value ;
if ( ! total | | ! value | | ! width )
return DM_PERCENT_0 ;
return dm_make_percent ( ( uint64_t ) val , total ) ;
}
/*
* Histogram string helper functions : used to construct histogram and
* bin boundary strings from numeric data .
*/
/*
* Allocate an unbound histogram object with nr_bins bins . Only used
* for histograms used to hold bounds values as arguments for calls to
* dm_stats_create_region ( ) .
*/
static struct dm_histogram * _alloc_dm_histogram ( int nr_bins )
{
struct dm_histogram * dmh = NULL ;
struct dm_histogram_bin * cur = NULL ;
/* Allocate space for dm_histogram + nr_entries. */
size_t size = sizeof ( * dmh ) + ( unsigned ) nr_bins * sizeof ( * cur ) ;
return dm_zalloc ( size ) ;
}
/*
* Parse a histogram bounds string supplied by the user . The string
* consists of a list of numbers , " n1,n2,n3,... " with optional ' ns ' ,
* ' us ' , ' ms ' , or ' s ' unit suffixes .
*
* The scale parameter indicates the timescale used for this region : one
* for nanoscale resolution and NSEC_PER_MSEC for miliseconds .
*
* On return bounds contains a pointer to an array of uint64_t
* histogram bounds values expressed in units of nanoseconds .
*/
struct dm_histogram * dm_histogram_bounds_from_string ( const char * bounds_str )
{
static const char * _valid_chars = " 0123456789,muns " ;
struct dm_histogram * dmh ;
struct dm_histogram_bin * cur ;
const char * c , * v ;
int nr_entries = 1 ;
c = bounds_str ;
/* Count number of bounds entries. */
while ( * c )
if ( * ( c + + ) = = ' , ' )
nr_entries + + ;
c = bounds_str ;
if ( ! ( dmh = _alloc_dm_histogram ( nr_entries ) ) )
return 0 ;
dmh - > nr_bins = nr_entries ;
cur = dmh - > bins ;
do {
for ( v = _valid_chars ; * v ; v + + )
if ( * c = = * v )
break ;
if ( ! * v )
goto badchar ;
if ( * c = = ' , ' ) {
log_error ( " Empty histogram bin not allowed: %s " ,
bounds_str ) ;
goto out ;
} else {
const char * val_start = c ;
char * endptr = NULL ;
uint64_t this_val = 0 , mult = 1 ;
this_val = strtoull ( val_start , & endptr , 10 ) ;
if ( ! endptr ) {
log_error ( " Could not parse histogram bound. " ) ;
goto out ;
}
c = endptr ; /* Advance to units, comma, or end. */
if ( * c = = ' s ' ) {
mult = NSEC_PER_SEC ;
c + + ; /* Advance over 's'. */
} else if ( * ( c + 1 ) = = ' s ' ) {
if ( * c = = ' m ' )
mult = NSEC_PER_MSEC ;
else if ( * c = = ' u ' )
mult = NSEC_PER_USEC ;
else if ( * c = = ' n ' )
mult = 1 ;
else
goto badchar ;
c + = 2 ; /* Advance over 'ms', 'us', or 'ns'. */
} else if ( * c = = ' , ' )
c + + ;
else if ( * c ) /* Expected ',' or NULL. */
goto badchar ;
if ( * c = = ' , ' )
c + + ;
this_val * = mult ;
( cur + + ) - > upper = this_val ;
}
} while ( * c ) ;
/* Bounds histograms have no owner. */
dmh - > dms = NULL ;
dmh - > region = NULL ;
return dmh ;
badchar :
log_error ( " Invalid character in histogram: %c " , * c ) ;
out :
dm_free ( dmh ) ;
return NULL ;
}
struct dm_histogram * dm_histogram_bounds_from_uint64 ( const uint64_t * bounds )
{
struct dm_histogram * dmh ;
struct dm_histogram_bin * cur ;
int nr_entries = 1 ;
const uint64_t * entry = bounds ;
if ( ! bounds | | ! bounds [ 0 ] ) {
log_error ( " Could not parse empty histogram bounds array " ) ;
return 0 ;
}
/* Count number of bounds entries. */
while ( * entry )
if ( * ( + + entry ) )
nr_entries + + ;
entry = bounds ;
if ( ! ( dmh = _alloc_dm_histogram ( nr_entries ) ) )
return_0 ;
dmh - > nr_bins = nr_entries ;
cur = dmh - > bins ;
while ( * entry )
( cur + + ) - > upper = * ( entry + + ) ;
/* Bounds histograms have no owner. */
dmh - > dms = NULL ;
dmh - > region = NULL ;
return dmh ;
}
void dm_histogram_bounds_destroy ( struct dm_histogram * bounds )
{
if ( ! bounds )
return ;
/* Bounds histograms are not bound to any handle or region. */
if ( bounds - > dms | | bounds - > region ) {
log_error ( " Freeing invalid histogram bounds pointer %p. " ,
( void * ) bounds ) ;
stack ;
}
/* dm_free() expects a (void *). */
dm_free ( ( void * ) bounds ) ;
}
/*
* Scale a bounds value down from nanoseconds to the largest possible
* whole unit suffix .
*/
static void _scale_bound_value_to_suffix ( uint64_t * bound , const char * * suffix )
{
if ( ! ( * bound % NSEC_PER_SEC ) ) {
* bound / = NSEC_PER_SEC ;
* suffix = " s " ;
} else if ( ! ( * bound % NSEC_PER_MSEC ) ) {
* bound / = NSEC_PER_MSEC ;
* suffix = " ms " ;
} else if ( ! ( * bound % NSEC_PER_USEC ) ) {
* bound / = NSEC_PER_USEC ;
* suffix = " us " ;
}
}
# define DM_HISTOGRAM_BOUNDS_MASK 0x30
static int _make_bounds_string ( char * buf , size_t size , uint64_t lower ,
uint64_t upper , int flags , int width )
{
const char * l_suff = NULL ;
const char * u_suff = NULL ;
2015-09-03 23:48:15 +03:00
const char * sep = " " ;
2015-08-19 22:39:10 +03:00
char bound_buf [ 32 ] ;
int bounds = flags & DM_HISTOGRAM_BOUNDS_MASK ;
if ( ! bounds )
return 0 ;
* buf = ' \0 ' ;
if ( flags & DM_HISTOGRAM_SUFFIX ) {
_scale_bound_value_to_suffix ( & lower , & l_suff ) ;
_scale_bound_value_to_suffix ( & upper , & u_suff ) ;
} else
l_suff = u_suff = " " ;
2015-09-03 23:48:15 +03:00
if ( flags & DM_HISTOGRAM_VALUES )
sep = " : " ;
2015-08-19 22:39:10 +03:00
if ( bounds > DM_HISTOGRAM_BOUNDS_LOWER ) {
/* Handle infinite uppermost bound. */
if ( upper = = UINT64_MAX ) {
if ( dm_snprintf ( bound_buf , sizeof ( bound_buf ) ,
" > " FMTu64 " %s " , lower , l_suff ) < 0 )
goto_out ;
/* Only display an 'upper' string for final bin. */
bounds = DM_HISTOGRAM_BOUNDS_UPPER ;
} else {
if ( dm_snprintf ( bound_buf , sizeof ( bound_buf ) ,
FMTu64 " %s " , upper , u_suff ) < 0 )
goto_out ;
}
} else if ( bounds = = DM_HISTOGRAM_BOUNDS_LOWER ) {
if ( ( dm_snprintf ( bound_buf , sizeof ( bound_buf ) , FMTu64 " %s " ,
lower , l_suff ) ) < 0 )
goto_out ;
}
switch ( bounds ) {
case DM_HISTOGRAM_BOUNDS_LOWER :
case DM_HISTOGRAM_BOUNDS_UPPER :
return dm_snprintf ( buf , size , " %*s%s " , width , bound_buf , sep ) ;
case DM_HISTOGRAM_BOUNDS_RANGE :
return dm_snprintf ( buf , size , FMTu64 " %s-%s%s " ,
lower , l_suff , bound_buf , sep ) ;
}
out :
return 0 ;
}
2015-09-03 23:48:15 +03:00
# define BOUND_WIDTH_NOSUFFIX 10 /* 999999999 nsecs */
2015-08-19 22:39:10 +03:00
# define BOUND_WIDTH 6 /* bounds string up to 9999xs */
# define COUNT_WIDTH 6 /* count string: up to 9999 */
2015-09-03 23:48:15 +03:00
# define PERCENT_WIDTH 6 /* percent string : 0.00-100.00% */
# define DM_HISTOGRAM_VALUES_MASK 0x06
2015-08-19 22:39:10 +03:00
const char * dm_histogram_to_string ( const struct dm_histogram * dmh , int bin ,
int width , int flags )
{
int minwidth , bounds , values , start , last ;
uint64_t lower , upper ; /* bounds of the current bin. */
/* Use the histogram pool for string building. */
struct dm_pool * mem = dmh - > dms - > hist_mem ;
char buf [ 64 ] , bounds_buf [ 64 ] ;
const char * sep = " " ;
ssize_t len ;
bounds = flags & DM_HISTOGRAM_BOUNDS_MASK ;
values = flags & DM_HISTOGRAM_VALUES ;
if ( bin < 0 ) {
start = 0 ;
last = dmh - > nr_bins - 1 ;
} else
start = last = bin ;
2015-09-03 23:48:15 +03:00
if ( width < 0 | | ! values )
width = minwidth = 0 ; /* no padding */
else if ( flags & DM_HISTOGRAM_PERCENT )
2015-08-19 22:39:10 +03:00
width = minwidth = ( width ) ? : PERCENT_WIDTH ;
else if ( flags & DM_HISTOGRAM_VALUES )
width = minwidth = ( width ) ? : COUNT_WIDTH ;
2015-09-03 23:48:15 +03:00
if ( values & & ! width )
sep = " : " ;
2015-08-19 22:39:10 +03:00
/* Set bounds string to the empty string. */
bounds_buf [ 0 ] = ' \0 ' ;
dm_pool_begin_object ( mem , 64 ) ;
for ( bin = start ; bin < = last ; bin + + ) {
if ( bounds ) {
2015-09-03 23:48:15 +03:00
int bounds_width ;
/* Default bounds width depends on time suffixes. */
bounds_width = ( ! ( flags & DM_HISTOGRAM_SUFFIX ) )
? BOUND_WIDTH_NOSUFFIX
: BOUND_WIDTH ;
bounds_width = ( ! width ) ? width : bounds_width ;
2015-08-19 22:39:10 +03:00
lower = dm_histogram_get_bin_lower ( dmh , bin ) ;
upper = dm_histogram_get_bin_upper ( dmh , bin ) ;
2015-09-03 23:48:15 +03:00
len = sizeof ( bounds_buf ) ;
len = _make_bounds_string ( bounds_buf , len ,
lower , upper , flags ,
bounds_width ) ;
/*
* Comma separates " bounds: value " pairs unless
* - - noheadings is used .
*/
sep = ( width | | ! values ) ? " , " : " : " ;
/* Adjust width by real bounds length if set. */
width - = ( width ) ? ( len - ( bounds_width + 1 ) ) : 0 ;
/* -ve width indicates specified width was overrun. */
2015-08-19 22:39:10 +03:00
width = ( width > 0 ) ? width : 0 ;
}
if ( bin = = last )
sep = " " ;
2015-09-03 23:48:15 +03:00
2015-08-19 22:39:10 +03:00
if ( flags & DM_HISTOGRAM_PERCENT ) {
dm_percent_t pr ;
float value ;
pr = dm_histogram_get_bin_percent ( dmh , bin ) ;
value = dm_percent_to_float ( pr ) ;
len = dm_snprintf ( buf , sizeof ( buf ) , " %s%*.2f%%%s " ,
bounds_buf , width , value , sep ) ;
} else if ( values ) {
uint64_t value = dmh - > bins [ bin ] . count ;
len = dm_snprintf ( buf , sizeof ( buf ) , " %s%* " PRIu64 " %s " ,
bounds_buf , width , value , sep ) ;
} else if ( bounds )
len = dm_snprintf ( buf , sizeof ( buf ) , " %s%s " , bounds_buf ,
sep ) ;
if ( len < 0 )
goto_out ;
width = minwidth ; /* re-set histogram column width. */
dm_pool_grow_object ( mem , buf , ( size_t ) len ) ;
}
dm_pool_grow_object ( mem , " \0 " , 1 ) ;
return ( const char * ) dm_pool_end_object ( mem ) ;
out :
dm_pool_abandon_object ( mem ) ;
return NULL ;
}
/*
* Backward compatible dm_stats_create_region ( ) implementations .
*
* Keep these at the end of the file to avoid adding clutter around the
* current dm_stats_create_region ( ) version .
*/
# if defined(__GNUC__)
int dm_stats_create_region_v1_02_106 ( struct dm_stats * dms , uint64_t * region_id ,
uint64_t start , uint64_t len , int64_t step ,
int precise , const char * program_id ,
const char * aux_data ) ;
int dm_stats_create_region_v1_02_106 ( struct dm_stats * dms , uint64_t * region_id ,
uint64_t start , uint64_t len , int64_t step ,
int precise , const char * program_id ,
const char * aux_data )
{
/* 1.02.106 lacks histogram argument. */
return _stats_create_region ( dms , region_id , start , len , step , precise ,
NULL , program_id , aux_data ) ;
}
DM_EXPORT_SYMBOL ( dm_stats_create_region , 1 _02_106 ) ;
int dm_stats_create_region_v1_02_104 ( struct dm_stats * dms , uint64_t * region_id ,
uint64_t start , uint64_t len , int64_t step ,
const char * program_id , const char * aux_data ) ;
int dm_stats_create_region_v1_02_104 ( struct dm_stats * dms , uint64_t * region_id ,
uint64_t start , uint64_t len , int64_t step ,
const char * program_id , const char * aux_data )
{
/* 1.02.104 lacks histogram and precise arguments. */
return _stats_create_region ( dms , region_id , start , len , step , 0 , NULL ,
program_id , aux_data ) ;
}
DM_EXPORT_SYMBOL ( dm_stats_create_region , 1 _02_104 ) ;
# endif