2015-08-05 12:40:00 +03:00
/*
2016-06-30 23:46:43 +03:00
* Copyright ( C ) 2016 Red Hat , Inc . All rights reserved .
*
* _stats_get_extents_for_file ( ) based in part on filefrag_fiemap ( ) from
* e2fsprogs / misc / filefrag . c . Copyright 2003 by Theodore Ts ' o .
2015-08-05 12:40:00 +03:00
*
* 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 ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2015-08-05 12:40:00 +03:00
*/
2018-05-14 12:30:20 +03:00
# include "libdm/misc/dmlib.h"
# include "libdm/misc/kdev_t.h"
2015-08-05 12:40:00 +03:00
2015-08-19 22:39:10 +03:00
# include "math.h" /* log10() */
2016-08-18 15:41:46 +03:00
# include <sys/sysmacros.h>
2016-06-30 23:46:43 +03:00
# include <sys/ioctl.h>
# include <sys/vfs.h> /* fstatfs */
2016-07-09 01:38:32 +03:00
# ifdef __linux__
# include <linux/fs.h> /* FS_IOC_FIEMAP */
# endif
# ifdef HAVE_LINUX_FIEMAP_H
# include <linux/fiemap.h> /* fiemap */
# endif
# ifdef HAVE_LINUX_MAGIC_H
# include <linux/magic.h> /* BTRFS_SUPER_MAGIC */
# endif
2016-06-30 23:46:43 +03:00
2015-08-05 12:40:00 +03:00
# define DM_STATS_REGION_NOT_PRESENT UINT64_MAX
2016-06-19 15:30:46 +03:00
# define DM_STATS_GROUP_NOT_PRESENT DM_STATS_GROUP_NONE
2015-08-05 12:40:00 +03:00
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:"
2016-06-16 21:40:43 +03:00
# define STATS_ROW_BUF_LEN 4096
# define STATS_MSG_BUF_LEN 1024
2016-07-08 23:13:25 +03:00
# define STATS_FIE_BUF_LEN 2048
2016-06-16 21:40:43 +03:00
2016-07-08 23:12:18 +03:00
# define SECTOR_SHIFT 9L
2015-08-19 22:39:10 +03:00
/* 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. */
2020-08-28 20:15:01 +03:00
struct dm_histogram_bin bins [ ] ;
2015-08-19 22:39:10 +03:00
} ;
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 */
2016-02-29 20:52:29 +03:00
uint64_t group_id ;
2015-08-05 12:40:00 +03:00
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 */
2016-07-03 23:59:31 +03:00
struct dm_histogram * histogram ; /* aggregate cache */
2015-08-05 12:40:00 +03:00
struct dm_stats_counters * counters ;
} ;
2016-02-29 20:52:29 +03:00
struct dm_stats_group {
uint64_t group_id ;
const char * alias ;
dm_bitset_t regions ;
2016-07-03 23:59:31 +03:00
struct dm_histogram * histogram ;
2016-02-29 20:52:29 +03:00
} ;
2015-08-05 12:40:00 +03:00
struct dm_stats {
/* device binding */
2016-03-10 19:51:02 +03:00
int bind_major ; /* device major that this dm_stats object is bound to */
int bind_minor ; /* device minor that this dm_stats object is bound to */
char * bind_name ; /* device-mapper device name */
char * bind_uuid ; /* device-mapper UUID */
2015-08-05 12:40:00 +03:00
char * program_id ; /* default program_id for this handle */
2016-06-17 14:17:33 +03:00
const char * name ; /* cached device_name used for reporting */
2015-08-05 12:40:00 +03:00
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 */
2016-02-29 20:52:29 +03:00
struct dm_pool * group_mem ; /* separate pool for group 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 ;
2016-02-29 20:52:29 +03:00
struct dm_stats_group * groups ;
2015-08-05 12:40:00 +03:00
/* statistics cursor */
2016-06-19 15:30:46 +03:00
uint64_t walk_flags ; /* walk control flags */
uint64_t cur_flags ;
uint64_t cur_group ;
2015-08-05 12:40:00 +03:00
uint64_t cur_region ;
uint64_t cur_area ;
} ;
2023-09-25 20:38:13 +03:00
static char * _stats_escape_aux_data ( const char * aux_data )
{
size_t aux_data_len = strlen ( aux_data ) ;
char * escaped = dm_malloc ( ( 3 * aux_data_len + 1 ) * sizeof ( char ) ) ;
2023-10-13 17:00:08 +03:00
size_t index = 0 , i ;
2023-09-25 20:38:13 +03:00
if ( ! escaped ) {
log_error ( " Could not allocate memory for escaped "
" aux_data string. " ) ;
return NULL ;
}
2023-10-13 17:00:08 +03:00
for ( i = 0 ; i < aux_data_len ; i + + ) {
2023-09-25 20:38:13 +03:00
if ( aux_data [ i ] = = ' ' ) {
escaped [ index + + ] = ' \\ ' ;
escaped [ index + + ] = ' ' ;
} else if ( aux_data [ i ] = = ' \\ ' ) {
escaped [ index + + ] = ' \\ ' ;
escaped [ index + + ] = ' \\ ' ;
} else if ( aux_data [ i ] = = ' \t ' ) {
escaped [ index + + ] = ' \\ ' ;
escaped [ index + + ] = ' \t ' ;
} else {
escaped [ index + + ] = aux_data [ i ] ;
}
}
escaped [ index ] = ' \0 ' ;
return escaped ;
}
2015-08-05 12:40:00 +03:00
# define PROC_SELF_COMM " / proc / self / comm"
static char * _program_id_from_proc ( void )
{
FILE * comm = NULL ;
2016-06-16 21:40:43 +03:00
char buf [ STATS_ROW_BUF_LEN ] ;
2015-08-05 12:40:00 +03:00
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-09-06 01:56:30 +03:00
if ( fclose ( comm ) )
2015-08-10 21:23:41 +03:00
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 ) ;
2016-02-29 20:52:29 +03:00
size_t group_hint = sizeof ( struct dm_stats_group ) ;
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-09-07 14:08:34 +03:00
if ( ! ( dms - > mem = dm_pool_create ( " stats_pool " , 4096 ) ) ) {
dm_free ( dms ) ;
return_NULL ;
}
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 ) ) )
2015-09-07 14:08:34 +03:00
goto_bad ;
2015-08-19 22:39:10 +03:00
2016-02-29 20:52:29 +03:00
if ( ! ( dms - > group_mem = dm_pool_create ( " group_pool " , group_hint ) ) )
goto_bad ;
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 ) ;
2015-09-07 14:08:34 +03:00
if ( ! dms - > program_id ) {
2016-07-04 20:20:09 +03:00
log_error ( " Could not allocate memory for program_id " ) ;
goto bad ;
2015-09-07 14:08:34 +03:00
}
2016-03-10 19:51:02 +03:00
dms - > bind_major = - 1 ;
dms - > bind_minor = - 1 ;
dms - > bind_name = NULL ;
dms - > bind_uuid = NULL ;
2015-08-05 12:40:00 +03:00
2016-06-17 14:17:33 +03:00
dms - > name = 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 ;
2016-06-19 15:30:46 +03:00
/* maintain compatibility with earlier walk version */
dms - > walk_flags = dms - > cur_flags = DM_STATS_WALK_DEFAULT ;
2015-08-05 12:40:00 +03:00
return dms ;
2015-09-06 01:56:30 +03:00
bad :
2015-09-07 14:08:34 +03:00
dm_pool_destroy ( dms - > mem ) ;
2016-02-29 20:52:29 +03:00
if ( dms - > hist_mem )
dm_pool_destroy ( dms - > hist_mem ) ;
if ( dms - > group_mem )
dm_pool_destroy ( dms - > group_mem ) ;
2015-08-10 12:15:22 +03:00
dm_free ( dms ) ;
return NULL ;
2015-08-05 12:40:00 +03:00
}
2016-02-29 20:52:29 +03:00
/*
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 ) ;
}
2016-02-29 20:52:29 +03:00
/*
* Test whether the stats group pointed to by group is present .
*/
static int _stats_group_present ( const struct dm_stats_group * group )
{
return ! ( group - > group_id = = DM_STATS_GROUP_NOT_PRESENT ) ;
}
/*
* Test whether a stats group id is present .
*/
static int _stats_group_id_present ( const struct dm_stats * dms , uint64_t id )
{
struct dm_stats_group * group = NULL ;
2017-01-25 18:10:59 +03:00
if ( id = = DM_STATS_GROUP_NOT_PRESENT )
return 0 ;
2017-03-09 20:33:02 +03:00
if ( ! dms )
2016-02-29 20:52:29 +03:00
return_0 ;
2017-03-09 20:33:02 +03:00
if ( ! dms - > regions )
return 0 ;
2016-02-29 20:52:29 +03:00
if ( id > dms - > max_region )
return 0 ;
group = & dms - > groups [ id ] ;
return _stats_group_present ( group ) ;
}
/*
* Test whether the given region_id is a member of any group .
*/
static uint64_t _stats_region_is_grouped ( const struct dm_stats * dms ,
uint64_t region_id )
{
uint64_t group_id ;
if ( region_id = = DM_STATS_GROUP_NOT_PRESENT )
return 0 ;
if ( ! _stats_region_present ( & dms - > regions [ region_id ] ) )
return 0 ;
group_id = dms - > regions [ region_id ] . group_id ;
return group_id ! = DM_STATS_GROUP_NOT_PRESENT ;
}
2015-08-19 22:39:10 +03:00
static void _stats_histograms_destroy ( struct dm_pool * mem ,
struct dm_stats_region * region )
{
/* Unpopulated handle. */
if ( ! region - > counters )
return ;
2015-09-07 19:53:56 +03:00
/*
2015-09-15 15:17:50 +03:00
* Free everything in the pool back to the first histogram .
2015-09-07 19:53:56 +03:00
*/
if ( region - > counters [ 0 ] . histogram )
dm_pool_free ( mem , region - > counters [ 0 ] . histogram ) ;
2015-08-19 22:39:10 +03:00
}
2015-08-05 12:40:00 +03:00
static void _stats_region_destroy ( struct dm_stats_region * region )
{
if ( ! _stats_region_present ( region ) )
return ;
2016-02-29 20:52:29 +03:00
region - > start = region - > len = region - > step = 0 ;
region - > timescale = 0 ;
/*
* Don ' t free counters and histogram bounds here : they are
* 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
*/
2016-02-29 20:52:29 +03:00
region - > counters = NULL ;
region - > bounds = NULL ;
2017-07-16 11:22:20 +03:00
dm_free ( region - > program_id ) ;
2016-02-29 20:52:29 +03:00
region - > program_id = NULL ;
2017-07-16 11:22:20 +03:00
dm_free ( region - > aux_data ) ;
2016-02-29 20:52:29 +03:00
region - > aux_data = NULL ;
region - > region_id = DM_STATS_REGION_NOT_PRESENT ;
2015-08-05 12:40:00 +03:00
}
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 ) ;
2016-12-09 18:47:55 +03:00
dms - > regions = NULL ;
2015-08-05 12:40:00 +03:00
}
2016-02-29 20:52:29 +03:00
static void _stats_group_destroy ( struct dm_stats_group * group )
{
if ( ! _stats_group_present ( group ) )
return ;
2016-07-03 23:59:31 +03:00
group - > histogram = NULL ;
2016-02-29 20:52:29 +03:00
if ( group - > alias ) {
dm_free ( ( char * ) group - > alias ) ;
group - > alias = NULL ;
}
if ( group - > regions ) {
dm_bitset_destroy ( group - > regions ) ;
group - > regions = NULL ;
}
group - > group_id = DM_STATS_GROUP_NOT_PRESENT ;
}
static void _stats_groups_destroy ( struct dm_stats * dms )
{
uint64_t i ;
if ( ! dms - > groups )
return ;
for ( i = dms - > max_region ; ( i ! = DM_STATS_REGION_NOT_PRESENT ) ; i - - )
_stats_group_destroy ( & dms - > groups [ i ] ) ;
dm_pool_free ( dms - > group_mem , dms - > groups ) ;
2016-12-09 18:49:06 +03:00
dms - > groups = NULL ;
2016-02-29 20:52:29 +03:00
}
2015-08-05 12:40:00 +03:00
static int _set_stats_device ( struct dm_stats * dms , struct dm_task * dmt )
{
2016-03-10 19:51:02 +03:00
if ( dms - > bind_name )
return dm_task_set_name ( dmt , dms - > bind_name ) ;
if ( dms - > bind_uuid )
return dm_task_set_uuid ( dmt , dms - > bind_uuid ) ;
if ( dms - > bind_major > 0 )
return dm_task_set_major ( dmt , dms - > bind_major )
& & dm_task_set_minor ( dmt , dms - > bind_minor ) ;
2015-08-05 12:40:00 +03:00
return_0 ;
}
2016-02-29 20:52:29 +03:00
static int _stats_bound ( const struct dm_stats * dms )
2015-08-05 12:40:00 +03:00
{
2016-03-10 19:51:02 +03:00
if ( dms - > bind_major > 0 | | dms - > bind_name | | dms - > bind_uuid )
2015-08-05 12:40:00 +03:00
return 1 ;
/* %p format specifier expects a void pointer. */
2020-08-28 20:35:25 +03:00
log_error ( " Stats handle at %p is not bound. " , ( const void * ) dms ) ;
2015-08-05 12:40:00 +03:00
return 0 ;
}
static void _stats_clear_binding ( struct dm_stats * dms )
{
2016-03-10 19:51:02 +03:00
if ( dms - > bind_name )
dm_pool_free ( dms - > mem , dms - > bind_name ) ;
if ( dms - > bind_uuid )
dm_pool_free ( dms - > mem , dms - > bind_uuid ) ;
2017-07-16 11:22:20 +03:00
dm_free ( ( char * ) dms - > name ) ;
2015-08-05 12:40:00 +03:00
2016-03-10 19:51:02 +03:00
dms - > bind_name = dms - > bind_uuid = NULL ;
dms - > bind_major = dms - > bind_minor = - 1 ;
2016-06-17 14:17:33 +03:00
dms - > name = NULL ;
2015-08-05 12:40:00 +03:00
}
int dm_stats_bind_devno ( struct dm_stats * dms , int major , int minor )
{
_stats_clear_binding ( dms ) ;
_stats_regions_destroy ( dms ) ;
2016-02-29 20:52:29 +03:00
_stats_groups_destroy ( dms ) ;
2015-08-05 12:40:00 +03:00
2016-03-10 19:51:02 +03:00
dms - > bind_major = major ;
dms - > bind_minor = minor ;
2015-08-05 12:40:00 +03:00
return 1 ;
}
int dm_stats_bind_name ( struct dm_stats * dms , const char * name )
{
_stats_clear_binding ( dms ) ;
_stats_regions_destroy ( dms ) ;
2016-02-29 20:52:29 +03:00
_stats_groups_destroy ( dms ) ;
2015-08-05 12:40:00 +03:00
2016-03-10 19:51:02 +03:00
if ( ! ( dms - > bind_name = dm_pool_strdup ( dms - > mem , name ) ) )
2015-08-05 12:40:00 +03:00
return_0 ;
return 1 ;
}
int dm_stats_bind_uuid ( struct dm_stats * dms , const char * uuid )
{
_stats_clear_binding ( dms ) ;
_stats_regions_destroy ( dms ) ;
2016-02-29 20:52:29 +03:00
_stats_groups_destroy ( dms ) ;
2015-08-05 12:40:00 +03:00
2016-03-10 19:51:02 +03:00
if ( ! ( dms - > bind_uuid = dm_pool_strdup ( dms - > mem , uuid ) ) )
2015-08-05 12:40:00 +03:00
return_0 ;
return 1 ;
}
2016-12-18 17:40:57 +03:00
int dm_stats_bind_from_fd ( struct dm_stats * dms , int fd )
{
int major , minor ;
struct stat buf ;
if ( fstat ( fd , & buf ) ) {
log_error ( " fstat failed for fd %d. " , fd ) ;
return 0 ;
}
major = ( int ) MAJOR ( buf . st_dev ) ;
minor = ( int ) MINOR ( buf . st_dev ) ;
if ( ! dm_stats_bind_devno ( dms , major , minor ) )
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 ) ;
}
2015-09-07 20:27:22 +03:00
static int _fill_hist_arg ( char * hist_arg , size_t hist_len , uint64_t scale ,
struct dm_histogram * bounds )
{
int i , l , len = 0 , nr_bins ;
char * arg = hist_arg ;
uint64_t value ;
nr_bins = bounds - > nr_bins ;
for ( i = 0 ; i < nr_bins ; i + + ) {
value = bounds - > bins [ i ] . upper / scale ;
if ( ( l = dm_snprintf ( arg , hist_len - len , FMTu64 " %s " , value ,
( i = = ( nr_bins - 1 ) ) ? " " : " , " ) ) < 0 )
return_0 ;
len + = l ;
arg + = l ;
}
return 1 ;
}
static void * _get_hist_arg ( struct dm_histogram * bounds , uint64_t scale ,
size_t * len )
{
struct dm_histogram_bin * entry , * bins ;
size_t hist_len = 1 ; /* terminating '\0' */
double value ;
entry = bins = bounds - > bins ;
entry + = bounds - > nr_bins - 1 ;
while ( entry > = bins ) {
value = ( double ) ( entry - - ) - > upper ;
/* Use lround to avoid size_t -> double cast warning. */
hist_len + = 1 + ( size_t ) lround ( log10 ( value / scale ) ) ;
if ( entry ! = bins )
hist_len + + ; /* ',' */
}
* len = hist_len ;
return dm_zalloc ( hist_len ) ;
}
2015-08-19 22:39:10 +03:00
static char * _build_histogram_arg ( struct dm_histogram * bounds , int * precise )
{
struct dm_histogram_bin * entry , * bins ;
2015-09-07 20:27:22 +03:00
size_t hist_len ;
char * hist_arg ;
2015-08-19 22:39:10 +03:00
uint64_t scale ;
entry = bins = bounds - > bins ;
/* Empty histogram is invalid. */
if ( ! bounds - > nr_bins ) {
log_error ( " Cannot format empty histogram description. " ) ;
return NULL ;
}
2015-09-07 20:27:22 +03:00
/* Validate entries and set *precise if precision < 1ms. */
2015-08-19 22:39:10 +03:00
entry + = bounds - > nr_bins - 1 ;
2015-09-07 20:27:22 +03:00
while ( entry > = bins ) {
2015-08-19 22:39:10 +03:00
if ( entry ! = bins ) {
if ( entry - > upper < ( entry - 1 ) - > upper ) {
log_error ( " Histogram boundaries must be in "
" order of increasing magnitude. " ) ;
return 0 ;
}
}
/*
* Only enable precise_timestamps automatically if any
* value in the histogram bounds uses precision < 1 ms .
*/
2015-09-07 20:27:22 +03:00
if ( ( ( entry - - ) - > upper % NSEC_PER_MSEC ) & & ! * precise )
2015-08-19 22:39:10 +03:00
* precise = 1 ;
}
2015-09-07 20:27:22 +03:00
scale = ( * precise ) ? 1 : NSEC_PER_MSEC ;
/* Calculate hist_len and allocate a character buffer. */
if ( ! ( hist_arg = _get_hist_arg ( bounds , scale , & hist_len ) ) ) {
2015-08-19 22:39:10 +03:00
log_error ( " Could not allocate memory for histogram argument. " ) ;
return 0 ;
}
2015-09-07 20:27:22 +03:00
/* Fill hist_arg with boundary strings. */
if ( ! _fill_hist_arg ( hist_arg , hist_len , scale , bounds ) )
goto_bad ;
2015-08-19 22:39:10 +03:00
return hist_arg ;
2015-09-06 01:56:30 +03:00
bad :
2015-08-19 22:39:10 +03:00
log_error ( " Could not build histogram arguments. " ) ;
dm_free ( hist_arg ) ;