mirror of
git://sourceware.org/git/lvm2.git
synced 2025-09-24 21:44:22 +03:00
Compare commits
15 Commits
v2_02_127
...
dev-bmr-dm
Author | SHA1 | Date | |
---|---|---|---|
|
63e6ef8491 | ||
|
a20989174f | ||
|
8df54e25c6 | ||
|
8ca04e316e | ||
|
e8eed522a8 | ||
|
cd8e3e28e5 | ||
|
8cc8b30ba7 | ||
|
1638e090fc | ||
|
e33656fc53 | ||
|
13d5b8100a | ||
|
077afe5b23 | ||
|
aa4d97d318 | ||
|
a4b5f8ef39 | ||
|
795c590980 | ||
|
ba2da29c4c |
@@ -1,6 +1,90 @@
|
||||
dm_report_add_header_row
|
||||
dm_report_clear
|
||||
dm_report_column_headings
|
||||
dm_report_get_interval
|
||||
dm_report_get_interval_ms
|
||||
dm_report_get_last_interval
|
||||
dm_report_header
|
||||
dm_report_header_set_content
|
||||
dm_report_output_headers
|
||||
dm_report_set_headers
|
||||
dm_report_set_interval
|
||||
dm_report_set_interval_ms
|
||||
dm_report_wait
|
||||
dm_size_to_string
|
||||
dm_stats_bind_devno
|
||||
dm_stats_bind_name
|
||||
dm_stats_bind_uuid
|
||||
dm_stats_buffer_destroy
|
||||
dm_stats_clear_region
|
||||
dm_stats_create
|
||||
dm_stats_create_region
|
||||
dm_stats_delete_region
|
||||
dm_stats_destroy
|
||||
dm_stats_get_area_len
|
||||
dm_stats_get_area_start
|
||||
dm_stats_get_average_queue_size
|
||||
dm_stats_get_average_rd_wait_time
|
||||
dm_stats_get_average_request_size
|
||||
dm_stats_get_average_wait_time
|
||||
dm_stats_get_average_wr_wait_time
|
||||
dm_stats_get_current_area
|
||||
dm_stats_get_current_area_len
|
||||
dm_stats_get_current_area_start
|
||||
dm_stats_get_current_region
|
||||
dm_stats_get_current_region_area_len
|
||||
dm_stats_get_current_region_aux_data
|
||||
dm_stats_get_current_region_len
|
||||
dm_stats_get_current_region_program_id
|
||||
dm_stats_get_current_region_start
|
||||
dm_stats_get_interval
|
||||
dm_stats_get_interval_ms
|
||||
dm_stats_get_io_in_progress
|
||||
dm_stats_get_io_nsecs
|
||||
dm_stats_get_rd_merges_per_sec
|
||||
dm_stats_get_read_nsecs
|
||||
dm_stats_get_reads
|
||||
dm_stats_get_read_sectors
|
||||
dm_stats_get_read_sectors_per_sec
|
||||
dm_stats_get_reads_merged
|
||||
dm_stats_get_reads_per_sec
|
||||
dm_stats_get_region_area_len
|
||||
dm_stats_get_region_aux_data
|
||||
dm_stats_get_region_len
|
||||
dm_stats_get_region_program_id
|
||||
dm_stats_get_region_start
|
||||
dm_stats_get_service_time
|
||||
dm_stats_get_throughput
|
||||
dm_stats_get_total_read_nsecs
|
||||
dm_stats_get_total_write_nsecs
|
||||
dm_stats_get_utilization
|
||||
dm_stats_get_weighted_io_nsecs
|
||||
dm_stats_get_write_nsecs
|
||||
dm_stats_get_writes
|
||||
dm_stats_get_write_sectors
|
||||
dm_stats_get_write_sectors_per_sec
|
||||
dm_stats_get_writes_merged
|
||||
dm_stats_get_writes_per_sec
|
||||
dm_stats_get_wr_merges_per_sec
|
||||
dm_stats_init
|
||||
dm_stats_list
|
||||
dm_stats_nr_areas
|
||||
dm_stats_nr_areas_current
|
||||
dm_stats_nr_areas_region
|
||||
dm_stats_nr_regions
|
||||
dm_stats_populate
|
||||
dm_stats_populate_region
|
||||
dm_stats_print_region
|
||||
dm_stats_region_present
|
||||
dm_stats_set_interval
|
||||
dm_stats_set_interval_ms
|
||||
dm_stats_set_program_id
|
||||
dm_stats_walk_end
|
||||
dm_stats_walk_next
|
||||
dm_stats_walk_next_region
|
||||
dm_stats_walk_start
|
||||
dm_timestamp_alloc
|
||||
dm_timestamp_compare
|
||||
dm_timestamp_get
|
||||
dm_timestamp_delta
|
||||
dm_timestamp_destroy
|
||||
dm_timestamp_get
|
||||
|
@@ -26,6 +26,7 @@ SOURCES =\
|
||||
libdm-string.c \
|
||||
libdm-report.c \
|
||||
libdm-timestamp.c \
|
||||
libdm-stats.c \
|
||||
libdm-config.c \
|
||||
mm/dbg_malloc.c \
|
||||
mm/pool.c \
|
||||
|
@@ -388,6 +388,421 @@ struct dm_status_thin {
|
||||
int dm_get_status_thin(struct dm_pool *mem, const char *params,
|
||||
struct dm_status_thin **status);
|
||||
|
||||
/**
|
||||
* device-mapper statistics support
|
||||
*/
|
||||
|
||||
/**
|
||||
* Statistics handle.
|
||||
*
|
||||
* Operations on dm_stats objects include managing statistics regions
|
||||
* and obtaining and manipulating current counter values from the
|
||||
* kernel.
|
||||
*/
|
||||
struct dm_stats;
|
||||
|
||||
/**
|
||||
* Allocate a dm_stats handle to use for subsequent device-mapper
|
||||
* statistics operations. A program_id may be specified and will be
|
||||
* used by default for subsequent operations on this handle.
|
||||
*
|
||||
* If program_id is NULL or the empty string a program_id will be
|
||||
* automatically set to the value contained in /proc/self/comm.
|
||||
*/
|
||||
struct dm_stats *dm_stats_create(const char *program_id);
|
||||
|
||||
/**
|
||||
* Bind a dm_stats handle to the specified device major and minor
|
||||
* values. Any previous binding is cleared and any preexisting counter
|
||||
* data contained in the handle is released.
|
||||
*/
|
||||
int dm_stats_bind_devno(struct dm_stats *dms, int major, int minor);
|
||||
|
||||
/**
|
||||
* Bind a dm_stats handle to the specified device name.
|
||||
* Any previous binding is cleared and any preexisting counter
|
||||
* data contained in the handle is released.
|
||||
*/
|
||||
int dm_stats_bind_name(struct dm_stats *dms, const char *name);
|
||||
|
||||
/**
|
||||
* Bind a dm_stats handle to the specified device UUID.
|
||||
* Any previous binding is cleared and any preexisting counter
|
||||
* data contained in the handle is released.
|
||||
*/
|
||||
int dm_stats_bind_uuid(struct dm_stats *dms, const char *uuid);
|
||||
|
||||
/**
|
||||
* Initialise a dm_stats handle so that it can contain at most
|
||||
* max_regions statistics regions. This must be called prior to
|
||||
* populating any regions with dm_stats_populate_region().
|
||||
*
|
||||
* If dm_stats_init() is called with a stats handle that was previously
|
||||
* initialised with dm_stats_init(), or another call that initialises
|
||||
* the region table (e.g. dm_stats_list()), any data contained in the
|
||||
* handle will be destroyed.
|
||||
*/
|
||||
int dm_stats_init(struct dm_stats *dms, uint64_t max_regions);
|
||||
|
||||
#define DM_STATS_ALL_PROGRAMS ""
|
||||
/*
|
||||
* Parse the response from a @stats_list message. dm_stats_list will
|
||||
* allocate the necessary dm_stats and dm_stats region structures from
|
||||
* the embedded dm_pool. No counter data will be obtained (the counters
|
||||
* members of dm_stats_region objects are set to NULL).
|
||||
*
|
||||
* A program_id may optionally be supplied; if the argument is non-NULL
|
||||
* only regions with a matching program_id value will be considered. If
|
||||
* the argument is NULL then the default program_id associated with the
|
||||
* dm_stats handle will be used. Passing the special value
|
||||
* DM_STATS_ALL_PROGRAMS will cause all regions to be queried
|
||||
* regardless of region program_id.
|
||||
*/
|
||||
int dm_stats_list(struct dm_stats *dms, const char *program_id);
|
||||
|
||||
/**
|
||||
* Populate a region of a dm_stats object with the response from
|
||||
* a @stats_print message.
|
||||
*/
|
||||
int dm_stats_populate_region(struct dm_stats *dms, uint64_t region_id,
|
||||
const char *resp);
|
||||
|
||||
#define DM_STATS_REGIONS_ALL UINT64_MAX
|
||||
/**
|
||||
* Populate a dm_stats object with statistics for one or more regions
|
||||
* of the specified device.
|
||||
*
|
||||
* A program_id may optionally be supplied; if the argument is non-NULL
|
||||
* only regions with a matching program_id value will be considered. If
|
||||
* the argument is NULL then the default program_id associated with the
|
||||
* dm_stats handle will be used. Passing the special value
|
||||
* DM_STATS_ALL_PROGRAMS will cause all regions to be queried
|
||||
* regardless of region program_id.
|
||||
*
|
||||
* Passing the special value DM_STATS_REGIONS_ALL as the region_id
|
||||
* argument will attempt to retrieve all regions selected by the
|
||||
* program_id argument.
|
||||
*
|
||||
* If region_id is used to request a single region_id to be populated
|
||||
* the program_id is ignored.
|
||||
*
|
||||
*/
|
||||
int dm_stats_populate(struct dm_stats *dms, const char *program_id,
|
||||
uint64_t region_id);
|
||||
|
||||
/**
|
||||
* Create a new statistics region on the device bound to dms.
|
||||
*
|
||||
* start and len specify the region start and length in 512b sectors.
|
||||
* Passing zero for both start and len will create a region spanning
|
||||
* the entire device.
|
||||
*
|
||||
* Step determines how to subdivide the region into discrete counter
|
||||
* sets: a positive value specifies the size of areas into which the
|
||||
* region should be split while a negative value will split the region
|
||||
* into a number of areas equal to the absolute value of step:
|
||||
*
|
||||
* - a region with one area spanning the entire device:
|
||||
*
|
||||
* dm_stats_create_region(dms, 0, 0, -1, p, a);
|
||||
*
|
||||
* - a region with areas of 1MiB:
|
||||
*
|
||||
* dm_stats_create_region(dms, 0, 0, 1 << 11, p, a);
|
||||
*
|
||||
* - one 1MiB region starting at 1024 sectors with two areas:
|
||||
*
|
||||
* dm_stats_create_region(dms, 1024, 1 << 11, -2, p, a);
|
||||
*
|
||||
* program_id is an optional string argument that identifies the
|
||||
* program creating the region. If program_id is NULL the default
|
||||
* program_id stored in the handle will be used.
|
||||
*
|
||||
* aux_data is an optional string argument passed to the kernel that is
|
||||
* stored with the statistics region. It is not accessed by the library
|
||||
* or kernel and may be used to store arbitrary user data.
|
||||
*
|
||||
* The region_id of the newly-created region is returned in *region_id
|
||||
* if it is non-NULL.
|
||||
*
|
||||
*/
|
||||
int dm_stats_create_region(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);
|
||||
|
||||
/**
|
||||
* Delete the specified statistics region. This will also mark the
|
||||
* region as not-present and discard any existing statistics data.
|
||||
*/
|
||||
int dm_stats_delete_region(struct dm_stats *dms, uint64_t region_id);
|
||||
|
||||
/**
|
||||
* Clear the specified statistics region. This requests the kernel to
|
||||
* zero all counter values (except in-flight I/O). Note that this
|
||||
* operation is not atomic with respect to reads of the counters; any IO
|
||||
* events occurring between the last print operation and the clear will
|
||||
* be lost. This can be avoided by using the atomic print-and-clear
|
||||
* function of the dm_stats_print_region() call or by using the higher
|
||||
* level dm_stats_populate*() interface.
|
||||
*/
|
||||
int dm_stats_clear_region(struct dm_stats *dms, uint64_t region_id);
|
||||
|
||||
/**
|
||||
* Print the current counter values for the specified statistics region
|
||||
* and return them as a string. The memory for the string buffer will
|
||||
* be allocated from the dm_stats handle's private pool and should be
|
||||
* returned by calling dm_stats_buffer_destroy() when no longer
|
||||
* required.
|
||||
*
|
||||
* This allows applications that wish to access the raw message response
|
||||
* to obtain it via a dm_stats handle; no parsing of the textual counter
|
||||
* data is carried out by this function.
|
||||
*
|
||||
* Most users are recommended to use the dm_stats_populate* calls
|
||||
* instead since these automatically parse the statistics data into
|
||||
* numeric form accessible via the dm_stats_get_*() counter access
|
||||
* methods.
|
||||
*
|
||||
* A subset of the data lines may be requested by setting the
|
||||
* start_line and num_lines parameters. If both are zero all data
|
||||
* lines are returned.
|
||||
*
|
||||
* If the clear parameter is non-zero the operation will also re-set
|
||||
* all counter values (except in-flight IO) to zero.
|
||||
*/
|
||||
char *dm_stats_print_region(struct dm_stats *dms, uint64_t region_id,
|
||||
unsigned start_line, unsigned num_lines,
|
||||
unsigned clear);
|
||||
|
||||
/**
|
||||
* Destroy a statistics message buffer obtained from a call to
|
||||
* dm_stats_print_region().
|
||||
*/
|
||||
void dm_stats_buffer_destroy(struct dm_stats *dms, char *buffer);
|
||||
|
||||
/**
|
||||
* Determine the number of regions contained in a dm_stats handle
|
||||
* following a dm_stats_list() or dm_stats_populate*() call.
|
||||
*
|
||||
* Always returns zero on an empty handle.
|
||||
*/
|
||||
int dm_stats_nr_regions(struct dm_stats *dms);
|
||||
|
||||
/**
|
||||
* Test whether region_id is present in this dm_stats handle.
|
||||
*/
|
||||
int dm_stats_region_present(struct dm_stats *dms, uint64_t region_id);
|
||||
|
||||
/*
|
||||
* Returns the number of areas (counter sets) contained in the specified
|
||||
* region_id of the supplied dm_stats handle.
|
||||
*/
|
||||
uint64_t dm_stats_nr_areas_region(struct dm_stats *dms, uint64_t region_id);
|
||||
|
||||
/*
|
||||
* Returns the number of areas (counter sets) contained in the current
|
||||
* region of the supplied dm_stats handle.
|
||||
*/
|
||||
uint64_t dm_stats_nr_areas_current(struct dm_stats *dms);
|
||||
|
||||
/**
|
||||
* Returns the total number of areas (counter sets) in all regions of the
|
||||
* given dm_stats object.
|
||||
*/
|
||||
uint64_t dm_stats_nr_areas(struct dm_stats *dms);
|
||||
|
||||
/**
|
||||
* Initialise the cursor of a dm_stats handle to address the first
|
||||
* present region. It is valid to attempt to walk a NULL stats handle
|
||||
* or a handle containing no present regions; in this case any call to
|
||||
* dm_stats_walk_next() becomes a no-op and all calls to
|
||||
* dm_stats_walk_end() return true.
|
||||
*/
|
||||
void dm_stats_walk_start(struct dm_stats *dms);
|
||||
|
||||
/**
|
||||
* Advance the statistics cursor to the next area, or to the next
|
||||
* present region if at the end of the current region.
|
||||
*/
|
||||
void dm_stats_walk_next(struct dm_stats *dms);
|
||||
|
||||
/**
|
||||
* Advance the statistics cursor to the next region.
|
||||
*/
|
||||
void dm_stats_walk_next_region(struct dm_stats *dms);
|
||||
|
||||
/**
|
||||
* Test whether the end of a statistics walk has been reached.
|
||||
*/
|
||||
int dm_stats_walk_end(struct dm_stats *dms);
|
||||
|
||||
/**
|
||||
* Stats iterators
|
||||
*
|
||||
* C 'for' and 'do'/'while' style iterators for dm_stats data.
|
||||
*
|
||||
* It is not safe to call any function that modifies the region table
|
||||
* within the loop body (i.e. dm_stats_list(), dm_stats_populate(),
|
||||
* dm_stats_init(), or dm_stats_destroy()).
|
||||
*
|
||||
* All counter and property (dm_stats_get_*) access methods, as well as
|
||||
* dm_stats_populate_region() can be safely called from loops.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Iterate over the regions table visiting each region.
|
||||
*
|
||||
* If the region table is empty or unpopulated the loop body will not be
|
||||
* executed.
|
||||
*/
|
||||
#define dm_stats_foreach_region(dms) \
|
||||
for (dm_stats_walk_start((dms)); \
|
||||
!dm_stats_walk_end((dms)); dm_stats_walk_next_region((dms)))
|
||||
|
||||
/**
|
||||
* Iterate over the regions table visiting each area.
|
||||
*
|
||||
* If the region table is empty or unpopulated the loop body will not
|
||||
* be executed.
|
||||
*/
|
||||
#define dm_stats_foreach_area(dms) \
|
||||
for (dm_stats_walk_start((dms)); \
|
||||
!dm_stats_walk_end((dms)); dm_stats_walk_next((dms)))
|
||||
|
||||
/**
|
||||
* Start a walk iterating over the regions contained in dm_stats handle
|
||||
* 'dms'.
|
||||
*
|
||||
* The body of the loop should call dm_stats_walk_next() or
|
||||
* dm_stats_walk_next_region() to advance to the next element.
|
||||
*
|
||||
* The loop body is executed at least once even if the stats handle is
|
||||
* empty.
|
||||
*/
|
||||
#define dm_stats_walk_do(dms) \
|
||||
dm_stats_walk_start((dms)); \
|
||||
do
|
||||
|
||||
/**
|
||||
* End a loop iterating over the regions contained in dm_stats handle
|
||||
* 'dms'.
|
||||
*/
|
||||
#define dm_stats_walk_while(dms) \
|
||||
while(!dm_stats_walk_end((dms)))
|
||||
|
||||
/**
|
||||
* Destroy a dm_stats object and all associated regions and counter
|
||||
* sets.
|
||||
*/
|
||||
void dm_stats_destroy(struct dm_stats *dms);
|
||||
|
||||
/**
|
||||
* Counter sampling interval
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set the sampling interval for counter data to the specified value in
|
||||
* either nanoseconds or miliseconds.
|
||||
*
|
||||
* The interval is used to calculate time-based metrics from the basic
|
||||
* counter data: an interval must be set before calling any of the
|
||||
* metric methods.
|
||||
*
|
||||
* For best accuracy the duration should be measured and updated at the
|
||||
* end of each interval.
|
||||
*
|
||||
* All values are stored internally with nanosecond precision and are
|
||||
* converted to or from ms when the milisecond interfaces are used.
|
||||
*/
|
||||
void dm_stats_set_interval(struct dm_stats *dms, uint64_t interval);
|
||||
void dm_stats_set_interval_ms(struct dm_stats *dms, uint64_t interval_ms);
|
||||
|
||||
/**
|
||||
* Retrieve the configured sampling interval in either nanoseconds or
|
||||
* miliseconds.
|
||||
*/
|
||||
uint64_t dm_stats_get_interval(struct dm_stats *dms);
|
||||
uint64_t dm_stats_get_interval_ms(struct dm_stats *dms);
|
||||
|
||||
/**
|
||||
* Override program_id. This may be used to change the default
|
||||
* program_id value for an existing handle. If the allow_empty argument
|
||||
* is non-zero a NULL or empty program_id is permitted.
|
||||
*
|
||||
* Use with caution! Most users of the library should set a valid,
|
||||
* non-NULL program_id for every statistics region created. Failing to
|
||||
* do so may result in confusing state when multiple programs are
|
||||
* creating and managing statistics regions.
|
||||
*
|
||||
* All users of the library are encouraged to choose an unambiguous,
|
||||
* unique program_id: this could be based on PID (for programs that
|
||||
* create, report, and delete regions in a single process), session id,
|
||||
* executable name, or some other distinguishing string.
|
||||
*
|
||||
* Use of the empty string as a program_id does not simplify use of the
|
||||
* library or the command line tools and use of this value is strongly
|
||||
* discouraged.
|
||||
*/
|
||||
int dm_stats_set_program_id(struct dm_stats *dms, int allow_empty,
|
||||
const char *program_id);
|
||||
|
||||
/**
|
||||
* Retrieve the current values of the stats cursor.
|
||||
*/
|
||||
uint64_t dm_stats_get_current_region(struct dm_stats *dms);
|
||||
uint64_t dm_stats_get_current_area(struct dm_stats *dms);
|
||||
|
||||
/**
|
||||
* Region properties: size, length & step.
|
||||
*
|
||||
* All values are returned in units of 512b sectors.
|
||||
*/
|
||||
int dm_stats_get_region_start(struct dm_stats *dms, uint64_t *start,
|
||||
uint64_t region_id);
|
||||
int dm_stats_get_region_len(struct dm_stats *dms, uint64_t *len,
|
||||
uint64_t region_id);
|
||||
int dm_stats_get_region_area_len(struct dm_stats *dms, uint64_t *area_len,
|
||||
uint64_t region_id);
|
||||
|
||||
int dm_stats_get_current_region_start(struct dm_stats *dms, uint64_t *start);
|
||||
int dm_stats_get_current_region_len(struct dm_stats *dms, uint64_t *len);
|
||||
int dm_stats_get_current_region_area_len(struct dm_stats *dms, uint64_t *area_len);
|
||||
|
||||
int dm_stats_get_area_start(struct dm_stats *dms, uint64_t *start,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
int dm_stats_get_area_len(struct dm_stats *dms, uint64_t *len,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_current_area_start(struct dm_stats *dms, uint64_t *start);
|
||||
int dm_stats_get_current_area_len(struct dm_stats *dms, uint64_t *start);
|
||||
|
||||
/**
|
||||
* Retrieve program_id and aux_data for a specific region. Only valid
|
||||
* following a call to dm_stats_list(). The returned pointer does not
|
||||
* need to be freed separately from the dm_stats handle but will become
|
||||
* invalid after a dm_stats_destroy(), dm_stats_list() or dm_stats_bind*()
|
||||
* of the handle from which it was obtained.
|
||||
*/
|
||||
const char *dm_stats_get_region_program_id(struct dm_stats *dms,
|
||||
uint64_t region_id);
|
||||
|
||||
const char *dm_stats_get_region_aux_data(struct dm_stats *dms,
|
||||
uint64_t region_id);
|
||||
|
||||
const char *dm_stats_get_current_region_program_id(struct dm_stats *dms);
|
||||
const char *dm_stats_get_current_region_aux_data(struct dm_stats *dms);
|
||||
|
||||
typedef enum {
|
||||
DM_STATS_MESSAGE_CREATE, /* region, step, [prog_id, [aux_data]] */
|
||||
DM_STATS_MESSAGE_DELETE, /* region_id */
|
||||
DM_STATS_MESSAGE_LIST, /* prog_id */
|
||||
DM_STATS_MESSAGE_CLEAR, /* region_id */
|
||||
DM_STATS_MESSAGE_PRINT, /* region_id [start_line, count] */
|
||||
DM_STATS_MESSAGE_PRINT_CLEAR, /* region_id */
|
||||
DM_STATS_MESSAGE_SET_AUX /* region_id aux_data */
|
||||
} dm_stats_message_t;
|
||||
|
||||
/*
|
||||
* Call this to actually run the ioctl.
|
||||
*/
|
||||
@@ -1743,6 +2158,7 @@ struct dm_report_field;
|
||||
|
||||
#define DM_REPORT_FIELD_TYPE_ID_LEN 32
|
||||
#define DM_REPORT_FIELD_TYPE_HEADING_LEN 32
|
||||
#define DM_REPORT_FIELD_TYPE_LABEL_LEN 32
|
||||
|
||||
struct dm_report;
|
||||
struct dm_report_field_type {
|
||||
@@ -1752,7 +2168,7 @@ struct dm_report_field_type {
|
||||
int32_t width; /* default width */
|
||||
/* string used to specify the field */
|
||||
const char id[DM_REPORT_FIELD_TYPE_ID_LEN];
|
||||
/* string printed in header */
|
||||
/* string printed in heading */
|
||||
const char heading[DM_REPORT_FIELD_TYPE_HEADING_LEN];
|
||||
int (*report_fn)(struct dm_report *rh, struct dm_pool *mem,
|
||||
struct dm_report_field *field, const void *data,
|
||||
@@ -1833,6 +2249,22 @@ typedef int (*dm_report_reserved_handler) (struct dm_report *rh,
|
||||
const void *data_in,
|
||||
const void **data_out);
|
||||
|
||||
struct dm_report_header;
|
||||
struct dm_report_header_type {
|
||||
uint32_t type; /* object type id */
|
||||
uint32_t flags; /* DM_REPORT_FIELD */
|
||||
uint32_t offset;
|
||||
int32_t width;
|
||||
/* string used to specify the header */
|
||||
const char id[DM_REPORT_FIELD_TYPE_ID_LEN];
|
||||
/* string printed in label*/
|
||||
const char label[DM_REPORT_FIELD_TYPE_LABEL_LEN];
|
||||
int (*report_fn)(struct dm_report *rh, struct dm_pool *mem,
|
||||
struct dm_report_header *header, const void *data,
|
||||
void *private_data);
|
||||
const char *desc; /* description of the header */
|
||||
};
|
||||
|
||||
/*
|
||||
* The dm_report_value_cache_{set,get} are helper functions to store and retrieve
|
||||
* various values used during reporting (dm_report_field_type.report_fn) and/or
|
||||
@@ -1851,6 +2283,8 @@ const void *dm_report_value_cache_get(struct dm_report *rh, const char *name);
|
||||
#define DM_REPORT_OUTPUT_FIELD_NAME_PREFIX 0x00000008
|
||||
#define DM_REPORT_OUTPUT_FIELD_UNQUOTED 0x00000010
|
||||
#define DM_REPORT_OUTPUT_COLUMNS_AS_ROWS 0x00000020
|
||||
#define DM_REPORT_OUTPUT_HEADERS 0x00000040
|
||||
#define DM_REPORT_OUTPUT_HEADER_LABELS 0x00000040
|
||||
|
||||
struct dm_report *dm_report_init(uint32_t *report_types,
|
||||
const struct dm_report_object_type *types,
|
||||
@@ -1882,6 +2316,23 @@ int dm_report_object(struct dm_report *rh, void *object);
|
||||
*/
|
||||
int dm_report_object_is_selected(struct dm_report *rh, void *object, int do_output, int *selected);
|
||||
|
||||
/*
|
||||
* Update header fields printed before each report.
|
||||
*/
|
||||
int dm_report_header(struct dm_report *rh, void *object);
|
||||
|
||||
/*
|
||||
* Add a row of headers to be output before each report. Currently only
|
||||
* a single row of header data is supported.
|
||||
*/
|
||||
int dm_report_add_header_row(struct dm_report *rh, const char *output_headers);
|
||||
|
||||
/*
|
||||
* Set the list of available header fields for this report.
|
||||
*/
|
||||
int dm_report_set_headers(struct dm_report *rh,
|
||||
const struct dm_report_header_type *headers);
|
||||
|
||||
/*
|
||||
* Compact report output so that if field value is empty for all rows in
|
||||
* the report, drop the field from output completely (including headers).
|
||||
@@ -1891,6 +2342,21 @@ int dm_report_object_is_selected(struct dm_report *rh, void *object, int do_outp
|
||||
int dm_report_compact_fields(struct dm_report *rh);
|
||||
|
||||
int dm_report_output(struct dm_report *rh);
|
||||
|
||||
int dm_report_output_headers(struct dm_report *rh);
|
||||
|
||||
/**
|
||||
* Clear the current report's data without reporting it.
|
||||
*/
|
||||
int dm_report_clear(struct dm_report *rh);
|
||||
|
||||
/**
|
||||
* Output the report headings for a columns-based report, even if they
|
||||
* have already been shown. Useful for repeating reports that wish to
|
||||
* issue a periodic reminder of the column headings.
|
||||
*/
|
||||
int dm_report_column_headings(struct dm_report *rh);
|
||||
|
||||
void dm_report_free(struct dm_report *rh);
|
||||
|
||||
/*
|
||||
@@ -1928,6 +2394,161 @@ int dm_report_field_percent(struct dm_report *rh, struct dm_report_field *field,
|
||||
void dm_report_field_set_value(struct dm_report_field *field, const void *value,
|
||||
const void *sortvalue);
|
||||
|
||||
/*
|
||||
* For custom header content, allocate the data in 'mem' and use
|
||||
* dm_report_header_set_content().
|
||||
*/
|
||||
void dm_report_header_set_content(struct dm_report_header *hdr,
|
||||
const void *content);
|
||||
|
||||
/*
|
||||
* Set an interval (in miliseconds) for this dm_report object that will
|
||||
* be used by any subsequent call to dm_report_wait_interval. This is
|
||||
* only useful for repeating reports (e.g. statistics).
|
||||
*
|
||||
* The default value is zero: no interval.
|
||||
*/
|
||||
void dm_report_set_interval(struct dm_report *rh, uint64_t interval);
|
||||
|
||||
/*
|
||||
* Set an interval in miliseconds for this dm_report object that will
|
||||
* be used by any subsequent call to dm_report_wait. This is only
|
||||
* useful for repeating reports (e.g. statistics).
|
||||
*
|
||||
* The default value is zero: no interval.
|
||||
*/
|
||||
void dm_report_set_interval_ms(struct dm_report *rh, uint64_t interval_ms);
|
||||
|
||||
/**
|
||||
* Retrieve the configured interval of the dm_report handle rh in
|
||||
* nanoseconds.
|
||||
*/
|
||||
uint64_t dm_report_get_interval(struct dm_report *rh);
|
||||
|
||||
/**
|
||||
* Retrieve the configured interval of the dm_report handle rh in
|
||||
* miliseconds.
|
||||
*/
|
||||
uint64_t dm_report_get_interval_ms(struct dm_report *rh);
|
||||
|
||||
/**
|
||||
* Retrieve the duration of the last interval of this dm_report handle
|
||||
* in nanoseconds.
|
||||
*/
|
||||
uint64_t dm_report_get_last_interval(struct dm_report *rh);
|
||||
|
||||
/*
|
||||
* Suspend the calling thread until the current reporting interval
|
||||
* expires. When this function returns the caller should obtain updated
|
||||
* report data and call dm_report_object() and dm_report_output() as
|
||||
* necessary in order to produce the new interval's reporting output.
|
||||
*
|
||||
* Delivery of a non-blocked signal to the thread carrying out the
|
||||
* wait will cause the function to return prematurely with an error.
|
||||
*
|
||||
* Attempting to wait on a report that has no interval set is also
|
||||
* treated as an error.
|
||||
*
|
||||
* If waited is non-NULL the actual duration of the wait in ns will
|
||||
* be returned.
|
||||
*/
|
||||
int dm_report_wait(struct dm_report *rh);
|
||||
|
||||
/**
|
||||
* Stats counter access methods
|
||||
*
|
||||
* Each method returns the corresponding stats counter value from the
|
||||
* supplied dm_stats handle for the specified region_id and area_id.
|
||||
* If either region_id or area_id uses one of the special values
|
||||
* DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT then the region
|
||||
* or area is selected according to the current state of the dm_stats
|
||||
* handle's embedded cursor.
|
||||
*/
|
||||
|
||||
#define DM_STATS_REGION_CURRENT UINT64_MAX
|
||||
#define DM_STATS_AREA_CURRENT UINT64_MAX
|
||||
|
||||
uint64_t dm_stats_get_reads(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_reads_merged(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_read_sectors(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_read_nsecs(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_writes(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_writes_merged(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_write_sectors(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_write_nsecs(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_io_in_progress(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_io_nsecs(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_weighted_io_nsecs(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_total_read_nsecs(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
uint64_t dm_stats_get_total_write_nsecs(struct dm_stats *dms,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_rd_merges_per_sec(struct dm_stats *dms, double *rrqm,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_wr_merges_per_sec(struct dm_stats *dms, double *rrqm,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_reads_per_sec(struct dm_stats *dms, double *rd_s,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_writes_per_sec(struct dm_stats *dms, double *wr_s,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_read_sectors_per_sec(struct dm_stats *dms, double *rsec_s,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_write_sectors_per_sec(struct dm_stats *dms, double *wr_s,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_average_request_size(struct dm_stats *dms, double *arqsz,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_service_time(struct dm_stats *dms, double *svctm,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_average_queue_size(struct dm_stats *dms, double *qusz,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_average_wait_time(struct dm_stats *dms, double *await,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_average_rd_wait_time(struct dm_stats *dms, double *await,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_average_wr_wait_time(struct dm_stats *dms, double *await,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_throughput(struct dm_stats *dms, double *tput,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
int dm_stats_get_utilization(struct dm_stats *dms, dm_percent_t *util,
|
||||
uint64_t region_id, uint64_t area_id);
|
||||
|
||||
/*************************
|
||||
* config file parse/print
|
||||
*************************/
|
||||
|
@@ -35,6 +35,12 @@ struct selection {
|
||||
struct dm_report {
|
||||
struct dm_pool *mem;
|
||||
|
||||
/**
|
||||
* Cache the first row allocated so that all rows and fields
|
||||
* can be disposed of in a single dm_pool_free() call.
|
||||
*/
|
||||
struct row *first_row;
|
||||
|
||||
/* To report all available types */
|
||||
#define REPORT_TYPES_ALL UINT32_MAX
|
||||
uint32_t report_types;
|
||||
@@ -42,15 +48,28 @@ struct dm_report {
|
||||
const char *field_prefix;
|
||||
uint32_t flags;
|
||||
const char *separator;
|
||||
/* reporting interval in ms */
|
||||
uint32_t interval;
|
||||
/* duration of last wait in ns */
|
||||
uint64_t last_wait;
|
||||
|
||||
uint32_t keys_count;
|
||||
|
||||
/* Ordered list of headers to be output before the report. */
|
||||
struct dm_list header_props;
|
||||
|
||||
/* Rows of headers */
|
||||
struct dm_list header_rows;
|
||||
|
||||
/* Ordered list of fields needed for this report */
|
||||
struct dm_list field_props;
|
||||
|
||||
/* Rows of report data */
|
||||
struct dm_list rows;
|
||||
|
||||
/* Array of header definitions */
|
||||
const struct dm_report_header_type *headers;
|
||||
|
||||
/* Array of field definitions */
|
||||
const struct dm_report_field_type *fields;
|
||||
const struct dm_report_object_type *types;
|
||||
@@ -85,6 +104,14 @@ struct field_properties {
|
||||
int implicit;
|
||||
};
|
||||
|
||||
struct header_properties {
|
||||
struct dm_list list;
|
||||
uint32_t hdr_num;
|
||||
int32_t width;
|
||||
const struct dm_report_object_type *type;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* Report selection
|
||||
*/
|
||||
@@ -222,6 +249,18 @@ struct row {
|
||||
int selected;
|
||||
};
|
||||
|
||||
struct dm_report_header {
|
||||
struct dm_list list;
|
||||
struct header_properties *props;
|
||||
const char *header_string;
|
||||
};
|
||||
|
||||
struct header_row {
|
||||
struct dm_list list;
|
||||
struct dm_report *rh;
|
||||
struct dm_list headers;
|
||||
};
|
||||
|
||||
/*
|
||||
* Implicit report types and fields.
|
||||
*/
|
||||
@@ -664,6 +703,11 @@ void dm_report_field_set_value(struct dm_report_field *field, const void *value,
|
||||
log_warn(INTERNAL_ERROR "Using string as sort value for numerical field.");
|
||||
}
|
||||
|
||||
void dm_report_header_set_content(struct dm_report_header *hdr, const void *content)
|
||||
{
|
||||
hdr->header_string = (const char *) content;
|
||||
}
|
||||
|
||||
static const char *_get_field_type_name(unsigned field_type)
|
||||
{
|
||||
switch (field_type) {
|
||||
@@ -692,6 +736,18 @@ static size_t _get_longest_field_id_len(const struct dm_report_field_type *field
|
||||
return id_len;
|
||||
}
|
||||
|
||||
static size_t _get_longest_header_id_len(const struct dm_report_header_type *headers)
|
||||
{
|
||||
uint32_t f;
|
||||
size_t id_len = 0;
|
||||
|
||||
for (f = 0; headers[f].report_fn; f++)
|
||||
if (strlen(headers[f].id) > id_len)
|
||||
id_len = strlen(headers[f].id);
|
||||
|
||||
return id_len;
|
||||
}
|
||||
|
||||
static void _display_fields_more(struct dm_report *rh,
|
||||
const struct dm_report_field_type *fields,
|
||||
size_t id_len, int display_all_fields_item,
|
||||
@@ -736,6 +792,45 @@ static void _display_fields_more(struct dm_report *rh,
|
||||
}
|
||||
}
|
||||
|
||||
static void _display_headers_more(struct dm_report *rh,
|
||||
const struct dm_report_header_type *headers,
|
||||
size_t id_len, int display_all_headers_item)
|
||||
{
|
||||
uint32_t h;
|
||||
const struct dm_report_object_type *type;
|
||||
const char *desc, *last_desc = "";
|
||||
|
||||
for (h = 0; headers[h].report_fn; h++)
|
||||
if (strlen(headers[h].id) > id_len)
|
||||
id_len = strlen(headers[h].id);
|
||||
|
||||
for (type = rh->types; type->data_fn; type++)
|
||||
if (strlen(type->prefix) + 3 > id_len)
|
||||
id_len = strlen(type->prefix) + 3;
|
||||
|
||||
for (h = 0; headers[h].report_fn; h++) {
|
||||
if ((type = _find_type(rh, headers[h].type)) && type->desc)
|
||||
desc = type->desc;
|
||||
else
|
||||
desc = " ";
|
||||
if (desc != last_desc) {
|
||||
if (*last_desc)
|
||||
log_warn(" ");
|
||||
log_warn("%s Fields", desc);
|
||||
log_warn("%*.*s", (int) strlen(desc) + 7,
|
||||
(int) strlen(desc) + 7,
|
||||
"-------------------------------------------------------------------------------");
|
||||
if (display_all_headers_item && type->id != SPECIAL_REPORT_TYPE)
|
||||
log_warn(" %sall%-*s - %s", type->prefix,
|
||||
(int) (id_len - 3 - strlen(type->prefix)), "",
|
||||
"All headers in this section.");
|
||||
}
|
||||
/* FIXME Add line-wrapping at terminal width (or 80 cols) */
|
||||
log_warn(" %-*s - %s", (int) id_len, headers[h].id, headers[h].desc);
|
||||
last_desc = desc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* show help message
|
||||
*/
|
||||
@@ -757,6 +852,20 @@ static void _display_fields(struct dm_report *rh, int display_all_fields_item,
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* show help message
|
||||
*/
|
||||
static void _display_headers(struct dm_report *rh, int display_all_headers_item,
|
||||
int display_header_types)
|
||||
{
|
||||
size_t tmp, id_len = 0;
|
||||
|
||||
if ((tmp = _get_longest_header_id_len(rh->headers)) > id_len)
|
||||
id_len = tmp;
|
||||
|
||||
_display_headers_more(rh, rh->headers, id_len, display_all_headers_item);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise report handle
|
||||
*/
|
||||
@@ -788,7 +897,7 @@ static struct field_properties * _add_field(struct dm_report *rh,
|
||||
{
|
||||
struct field_properties *fp;
|
||||
|
||||
if (!(fp = dm_pool_zalloc(rh->mem, sizeof(struct field_properties)))) {
|
||||
if (!(fp = dm_pool_zalloc(rh->mem, sizeof(*fp)))) {
|
||||
log_error("dm_report: struct field_properties allocation "
|
||||
"failed");
|
||||
return NULL;
|
||||
@@ -814,12 +923,55 @@ static struct field_properties * _add_field(struct dm_report *rh,
|
||||
return fp;
|
||||
}
|
||||
|
||||
static int _copy_header(struct dm_report *rh, struct header_properties *dest,
|
||||
uint32_t hdr_num)
|
||||
{
|
||||
const struct dm_report_header_type *headers = rh->headers;
|
||||
|
||||
dest->hdr_num = hdr_num;
|
||||
dest->width = headers[hdr_num].width;
|
||||
dest->flags = headers[hdr_num].flags & DM_REPORT_FIELD_MASK;
|
||||
|
||||
/* set object type method */
|
||||
dest->type = _find_type(rh, headers[hdr_num].type);
|
||||
if (!dest->type) {
|
||||
log_error("dm_report: no matching header: %s",
|
||||
headers[hdr_num].id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct header_properties * _add_header(struct dm_report *rh,
|
||||
uint32_t hdr_num, uint32_t flags)
|
||||
{
|
||||
struct header_properties *hp;
|
||||
|
||||
if (!(hp = dm_pool_zalloc(rh->mem, sizeof(*hp)))) {
|
||||
log_error("dm_report: struct header_properties allocation "
|
||||
"failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_copy_header(rh, hp, hdr_num)) {
|
||||
stack;
|
||||
dm_pool_free(rh->mem, hp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hp->flags |= flags;
|
||||
dm_list_add(&rh->header_props, &hp->list);
|
||||
|
||||
return hp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare name1 against name2 or prefix plus name2
|
||||
* name2 is not necessarily null-terminated.
|
||||
* len2 is the length of name2.
|
||||
*/
|
||||
static int _is_same_field(const char *name1, const char *name2,
|
||||
static int _is_same_name(const char *name1, const char *name2,
|
||||
size_t len2, const char *prefix)
|
||||
{
|
||||
size_t prefix_len;
|
||||
@@ -905,7 +1057,7 @@ static int _get_field(struct dm_report *rh, const char *field, size_t flen,
|
||||
return 0;
|
||||
|
||||
for (f = 0; _implicit_report_fields[f].report_fn; f++) {
|
||||
if (_is_same_field(_implicit_report_fields[f].id, field, flen, rh->field_prefix)) {
|
||||
if (_is_same_name(_implicit_report_fields[f].id, field, flen, rh->field_prefix)) {
|
||||
*f_ret = f;
|
||||
*implicit = 1;
|
||||
return 1;
|
||||
@@ -913,7 +1065,7 @@ static int _get_field(struct dm_report *rh, const char *field, size_t flen,
|
||||
}
|
||||
|
||||
for (f = 0; rh->fields[f].report_fn; f++) {
|
||||
if (_is_same_field(rh->fields[f].id, field, flen, rh->field_prefix)) {
|
||||
if (_is_same_name(rh->fields[f].id, field, flen, rh->field_prefix)) {
|
||||
*f_ret = f;
|
||||
*implicit = 0;
|
||||
return 1;
|
||||
@@ -952,6 +1104,51 @@ static int _field_match(struct dm_report *rh, const char *field, size_t flen,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add all headers with a matching type.
|
||||
*/
|
||||
static int _add_all_headers(struct dm_report *rh, uint32_t type)
|
||||
{
|
||||
uint32_t h;
|
||||
|
||||
for (h = 0; rh->headers[h].report_fn; h++)
|
||||
if ((rh->headers[h].type & type) && !_add_header(rh, h, 0))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _get_header(struct dm_report *rh, const char *header,
|
||||
size_t hlen, uint32_t *h_ret)
|
||||
{
|
||||
uint32_t h;
|
||||
|
||||
if (!hlen)
|
||||
return 0;
|
||||
|
||||
for (h = 0; rh->headers[h].report_fn; h++) {
|
||||
if (_is_same_name(rh->headers[h].id, header, hlen, "")) {
|
||||
*h_ret = h;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _header_match(struct dm_report *rh, const char *header, size_t hlen)
|
||||
{
|
||||
uint32_t h /*, type*/;
|
||||
|
||||
if (!hlen)
|
||||
return 0;
|
||||
|
||||
if ((_get_header(rh, header, hlen, &h)))
|
||||
return _add_header(rh, h, 0) ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _add_sort_key(struct dm_report *rh, uint32_t field_num, int implicit,
|
||||
uint32_t flags, unsigned report_type_only)
|
||||
{
|
||||
@@ -1015,11 +1212,11 @@ static int _key_match(struct dm_report *rh, const char *key, size_t len,
|
||||
}
|
||||
|
||||
for (f = 0; _implicit_report_fields[f].report_fn; f++)
|
||||
if (_is_same_field(_implicit_report_fields[f].id, key, len, rh->field_prefix))
|
||||
if (_is_same_name(_implicit_report_fields[f].id, key, len, rh->field_prefix))
|
||||
return _add_sort_key(rh, f, 1, flags, report_type_only);
|
||||
|
||||
for (f = 0; rh->fields[f].report_fn; f++)
|
||||
if (_is_same_field(rh->fields[f].id, key, len, rh->field_prefix))
|
||||
if (_is_same_name(rh->fields[f].id, key, len, rh->field_prefix))
|
||||
return _add_sort_key(rh, f, 0, flags, report_type_only);
|
||||
|
||||
return 0;
|
||||
@@ -1079,6 +1276,32 @@ static int _parse_keys(struct dm_report *rh, const char *keys,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _parse_headers(struct dm_report *rh, const char *format)
|
||||
{
|
||||
const char *ws; /* Word start */
|
||||
const char *we = format; /* Word end */
|
||||
|
||||
while (*we) {
|
||||
/* Allow consecutive commas */
|
||||
while (*we && *we == ',')
|
||||
we++;
|
||||
|
||||
/* start of the header name */
|
||||
ws = we;
|
||||
while (*we && *we != ',')
|
||||
we++;
|
||||
|
||||
if (!_header_match(rh, ws, (size_t) (we - ws))) {
|
||||
_display_headers(rh, 1, 0);
|
||||
log_warn(" ");
|
||||
log_error("Unrecognised header: %.*s", (int) (we - ws), ws);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _contains_reserved_report_type(const struct dm_report_object_type *types)
|
||||
{
|
||||
const struct dm_report_object_type *type, *implicit_type;
|
||||
@@ -1159,6 +1382,7 @@ struct dm_report *dm_report_init(uint32_t *report_types,
|
||||
rh->fields = fields;
|
||||
rh->types = types;
|
||||
rh->private = private_data;
|
||||
rh->interval = 0; /* no interval */
|
||||
|
||||
rh->flags |= output_flags & DM_REPORT_OUTPUT_MASK;
|
||||
|
||||
@@ -1174,6 +1398,8 @@ struct dm_report *dm_report_init(uint32_t *report_types,
|
||||
rh->flags |= RH_SORT_REQUIRED;
|
||||
|
||||
dm_list_init(&rh->field_props);
|
||||
dm_list_init(&rh->header_props);
|
||||
dm_list_init(&rh->header_rows);
|
||||
dm_list_init(&rh->rows);
|
||||
|
||||
if ((type = _find_type(rh, rh->report_types)) && type->prefix)
|
||||
@@ -1240,6 +1466,21 @@ static char *_toupperstr(char *str)
|
||||
return str;
|
||||
}
|
||||
|
||||
int dm_report_set_headers(struct dm_report *rh,
|
||||
const struct dm_report_header_type *headers)
|
||||
{
|
||||
if (headers)
|
||||
rh->headers = headers;
|
||||
else
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dm_report_add_header_row(struct dm_report *rh, const char *output_headers)
|
||||
{
|
||||
return _parse_headers(rh, output_headers);
|
||||
}
|
||||
|
||||
int dm_report_set_output_field_name_prefix(struct dm_report *rh, const char *output_field_name_prefix)
|
||||
{
|
||||
char *prefix;
|
||||
@@ -1280,6 +1521,21 @@ static void *_report_get_implicit_field_data(struct dm_report *rh __attribute__(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create part of a line of header data
|
||||
*/
|
||||
static void *_report_get_header_data(struct dm_report *rh, struct
|
||||
header_properties *hp, void *object)
|
||||
{
|
||||
const struct dm_report_header_type *headers = rh->headers;
|
||||
char *ret = hp->type->data_fn(object);
|
||||
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
return (void *)(ret + headers[hp->hdr_num].offset);
|
||||
}
|
||||
|
||||
static int _dbl_equal(double d1, double d2)
|
||||
{
|
||||
return fabs(d1 - d2) < DBL_EPSILON;
|
||||
@@ -1801,6 +2057,9 @@ static int _do_report_object(struct dm_report *rh, void *object, int do_output,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!rh->first_row)
|
||||
rh->first_row = row;
|
||||
|
||||
row->rh = rh;
|
||||
|
||||
if ((rh->flags & RH_SORT_REQUIRED) &&
|
||||
@@ -1967,6 +2226,82 @@ int dm_report_object_is_selected(struct dm_report *rh, void *object, int do_outp
|
||||
return _do_report_object(rh, object, do_output, selected);
|
||||
}
|
||||
|
||||
int dm_report_header(struct dm_report *rh, void *object)
|
||||
{
|
||||
const struct dm_report_header_type *headers;
|
||||
struct header_properties *hp;
|
||||
struct header_row *row = NULL;
|
||||
struct dm_report_header *header;
|
||||
void *data = NULL;
|
||||
int len;
|
||||
int r = 0;
|
||||
|
||||
if (!rh) {
|
||||
log_error(INTERNAL_ERROR "dm_report_header: dm_report handler is NULL.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rh->flags & RH_ALREADY_REPORTED)
|
||||
return 1;
|
||||
|
||||
if (!(row = dm_pool_zalloc(rh->mem, sizeof(*row)))) {
|
||||
log_error("dm_report_header: struct header_row allocation failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
row->rh = rh;
|
||||
headers = rh->headers;
|
||||
dm_list_init(&row->headers);
|
||||
|
||||
/* For each field to be displayed, call its report_fn */
|
||||
dm_list_iterate_items(hp, &rh->header_props) {
|
||||
if (!(header = dm_pool_zalloc(rh->mem, sizeof(*header)))) {
|
||||
log_error("do_report_header: "
|
||||
"struct dm_report_header allocation failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
header->props = hp;
|
||||
|
||||
data = _report_get_header_data(rh, hp, object);
|
||||
if (!data) {
|
||||
log_error("do_report_header:"
|
||||
"no data assigned to header %s",
|
||||
headers[hp->hdr_num].id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!headers[hp->hdr_num].report_fn(rh, rh->mem,
|
||||
header, data,
|
||||
rh->private)) {
|
||||
log_error("do_report_header:"
|
||||
"report function failed for header %s",
|
||||
headers[hp->hdr_num].id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dm_list_add(&row->headers, &header->list);
|
||||
}
|
||||
|
||||
r = 1;
|
||||
|
||||
dm_list_add(&rh->header_rows, &row->list);
|
||||
|
||||
dm_list_iterate_items(header, &row->headers) {
|
||||
len = (int) strlen(header->header_string);
|
||||
if ((len > header->props->width))
|
||||
header->props->width = len;
|
||||
}
|
||||
|
||||
if (!(rh->flags & DM_REPORT_OUTPUT_BUFFERED))
|
||||
return dm_report_output_headers(rh);
|
||||
|
||||
out:
|
||||
if (!r)
|
||||
dm_pool_free(rh->mem, row);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Selection parsing
|
||||
*/
|
||||
@@ -3835,9 +4170,6 @@ static int _report_headings(struct dm_report *rh)
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
|
||||
if (rh->flags & RH_HEADINGS_PRINTED)
|
||||
return 1;
|
||||
|
||||
rh->flags |= RH_HEADINGS_PRINTED;
|
||||
|
||||
if (!(rh->flags & DM_REPORT_OUTPUT_HEADINGS))
|
||||
@@ -3894,8 +4226,12 @@ static int _report_headings(struct dm_report *rh)
|
||||
log_error("dm_report: Failed to generate report headings for printing");
|
||||
goto bad;
|
||||
}
|
||||
log_print("%s", (char *) dm_pool_end_object(rh->mem));
|
||||
|
||||
/* print all headings */
|
||||
heading = (char *) dm_pool_end_object(rh->mem);
|
||||
log_print("%s", heading);
|
||||
|
||||
dm_pool_free(rh->mem, (void *)heading);
|
||||
dm_free(buf);
|
||||
|
||||
return 1;
|
||||
@@ -3906,6 +4242,11 @@ static int _report_headings(struct dm_report *rh)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_report_column_headings(struct dm_report *rh)
|
||||
{
|
||||
return _report_headings(rh);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort rows of data
|
||||
*/
|
||||
@@ -4084,6 +4425,86 @@ bad:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Produce header output
|
||||
*/
|
||||
static int _output_header(struct dm_report *rh, struct dm_report_header *header)
|
||||
{
|
||||
int32_t width, labelwidth;
|
||||
uint32_t align;
|
||||
const char *hdrstr, *labelstr;
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
|
||||
if (rh->flags & DM_REPORT_OUTPUT_HEADER_LABELS)
|
||||
labelstr = rh->headers[header->props->hdr_num].label;
|
||||
else
|
||||
labelstr = "";
|
||||
|
||||
labelwidth = strlen(labelstr) + 1;
|
||||
hdrstr = header->header_string;
|
||||
width = header->props->width - labelwidth;
|
||||
|
||||
if (!(rh->flags & DM_REPORT_OUTPUT_ALIGNED)) {
|
||||
if (!dm_pool_grow_object(rh->mem, hdrstr, 0)) {
|
||||
log_error("dm_report: Unable to extend output line");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (!(align = header->props->flags & DM_REPORT_FIELD_ALIGN_MASK))
|
||||
align = ((header->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) ||
|
||||
(header->props->flags & DM_REPORT_FIELD_TYPE_SIZE)) ?
|
||||
DM_REPORT_FIELD_ALIGN_RIGHT : DM_REPORT_FIELD_ALIGN_LEFT;
|
||||
|
||||
/* Including trailing '\0'! */
|
||||
buf_size = labelwidth + width + 1;
|
||||
if (!(buf = dm_malloc(buf_size))) {
|
||||
log_error("dm_report: Could not allocate memory for output line buffer.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (align & DM_REPORT_FIELD_ALIGN_LEFT) {
|
||||
if (dm_snprintf(buf, buf_size, "%-*.*s%-*.*s",
|
||||
/* FIXME: handle label width better */
|
||||
labelwidth, labelwidth, labelstr, width, width, hdrstr) < 0) {
|
||||
log_error("dm_report: left-aligned snprintf() failed");
|
||||
goto bad;
|
||||
}
|
||||
if (!dm_pool_grow_object(rh->mem, buf, labelwidth + width)) {
|
||||
log_error("dm_report: Unable to extend output line");
|
||||
goto bad;
|
||||
}
|
||||
} else if (align & DM_REPORT_FIELD_ALIGN_RIGHT) {
|
||||
if (dm_snprintf(buf, buf_size, "%*.*s%*.*s",
|
||||
/* FIXME: handle label width better */
|
||||
labelwidth, labelwidth, labelstr, width, width, hdrstr) < 0) {
|
||||
log_error("dm_report: right-aligned snprintf() failed");
|
||||
goto bad;
|
||||
}
|
||||
if (!dm_pool_grow_object(rh->mem, buf, labelwidth + width)) {
|
||||
log_error("dm_report: Unable to extend output line");
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dm_free(buf);
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
dm_free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _destroy_rows(struct dm_report *rh)
|
||||
{
|
||||
/* free the first row allocated to this report */
|
||||
if(rh->first_row)
|
||||
dm_pool_free(rh->mem, rh->first_row);
|
||||
rh->first_row = NULL;
|
||||
dm_list_init(&rh->rows);
|
||||
}
|
||||
|
||||
static int _output_as_rows(struct dm_report *rh)
|
||||
{
|
||||
const struct dm_report_field_type *fields;
|
||||
@@ -4139,6 +4560,8 @@ static int _output_as_rows(struct dm_report *rh)
|
||||
log_print("%s", (char *) dm_pool_end_object(rh->mem));
|
||||
}
|
||||
|
||||
_destroy_rows(rh);
|
||||
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
@@ -4187,8 +4610,7 @@ static int _output_as_columns(struct dm_report *rh)
|
||||
dm_list_del(&row->list);
|
||||
}
|
||||
|
||||
if (row)
|
||||
dm_pool_free(rh->mem, row);
|
||||
_destroy_rows(rh);
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -4197,6 +4619,27 @@ static int _output_as_columns(struct dm_report *rh)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_report_clear(struct dm_report *rh)
|
||||
{
|
||||
struct dm_list *fh, *rowh, *ftmp, *rtmp;
|
||||
struct row *row = NULL;
|
||||
struct dm_report_field *field;
|
||||
|
||||
/* clear buffer */
|
||||
dm_list_iterate_safe(rowh, rtmp, &rh->rows) {
|
||||
row = dm_list_item(rowh, struct row);
|
||||
dm_list_iterate_safe(fh, ftmp, &row->fields) {
|
||||
field = dm_list_item(fh, struct dm_report_field);
|
||||
dm_list_del(&field->list);
|
||||
}
|
||||
dm_list_del(&row->list);
|
||||
}
|
||||
|
||||
_destroy_rows(rh);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dm_report_output(struct dm_report *rh)
|
||||
{
|
||||
if (dm_list_empty(&rh->rows))
|
||||
@@ -4210,3 +4653,128 @@ int dm_report_output(struct dm_report *rh)
|
||||
else
|
||||
return _output_as_columns(rh);
|
||||
}
|
||||
|
||||
int dm_report_output_headers(struct dm_report *rh)
|
||||
{
|
||||
struct dm_list *hh, *rowh, *htmp;
|
||||
struct header_row *row = NULL;
|
||||
struct dm_report_header *header;
|
||||
|
||||
/* Print and clear buffer */
|
||||
dm_list_iterate_safe(rowh, htmp, &rh->header_rows) {
|
||||
if (!dm_pool_begin_object(rh->mem, 512)) {
|
||||
log_error("dm_report: Unable to allocate output line");
|
||||
return 0;
|
||||
}
|
||||
row = dm_list_item(rowh, struct header_row);
|
||||
|
||||
/* don't attempt to print an empty header row. */
|
||||
if (dm_list_empty(&row->headers)) {
|
||||
dm_pool_abandon_object(rh->mem);
|
||||
dm_list_del(&row->list);
|
||||
continue;
|
||||
}
|
||||
|
||||
dm_list_iterate_safe(hh, htmp, &row->headers) {
|
||||
header = dm_list_item(hh, struct dm_report_header);
|
||||
|
||||
if (!_output_header(rh, header))
|
||||
goto bad;
|
||||
|
||||
dm_list_del(&header->list);
|
||||
if (!dm_list_end(&row->headers, hh))
|
||||
if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
|
||||
log_error("dm_report: Unable to extend header output line");
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
|
||||
log_error("dm_report: Unable to terminate header output line");
|
||||
goto bad;
|
||||
}
|
||||
log_print("%s", (char *) dm_pool_end_object(rh->mem));
|
||||
dm_list_del(&row->list);
|
||||
if (dm_list_end(&rh->header_rows, rowh))
|
||||
break;
|
||||
}
|
||||
|
||||
dm_pool_free(rh->mem, row);
|
||||
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
dm_pool_abandon_object(rh->mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NSEC_PER_USEC 1000L
|
||||
#define NSEC_PER_MSEC 1000000L
|
||||
#define NSEC_PER_SEC 1000000000L
|
||||
|
||||
void dm_report_set_interval(struct dm_report *rh, uint64_t interval)
|
||||
{
|
||||
rh->interval = interval;
|
||||
}
|
||||
|
||||
void dm_report_set_interval_ms(struct dm_report *rh, uint64_t interval_ms)
|
||||
{
|
||||
rh->interval = interval_ms * NSEC_PER_MSEC;
|
||||
}
|
||||
|
||||
uint64_t dm_report_get_interval(struct dm_report *rh)
|
||||
{
|
||||
return rh->interval;
|
||||
}
|
||||
|
||||
uint64_t dm_report_get_interval_ms(struct dm_report *rh)
|
||||
{
|
||||
return (rh->interval / NSEC_PER_MSEC);
|
||||
}
|
||||
|
||||
uint64_t dm_report_get_last_interval(struct dm_report *rh)
|
||||
{
|
||||
if (rh->last_wait)
|
||||
return rh->last_wait;
|
||||
return rh->interval;
|
||||
}
|
||||
|
||||
int dm_report_wait(struct dm_report *rh)
|
||||
{
|
||||
struct dm_timestamp *ts_start, *ts_now;
|
||||
int r = 1;
|
||||
|
||||
if (!rh->interval)
|
||||
return_0;
|
||||
|
||||
if(!(ts_start = dm_timestamp_alloc()))
|
||||
return_0;
|
||||
|
||||
if(!(ts_now = dm_timestamp_alloc()))
|
||||
return_0;
|
||||
|
||||
if (!dm_timestamp_get(ts_start)) {
|
||||
log_error("Could not obtain initial timestamp.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usleep(rh->interval / NSEC_PER_USEC)) {
|
||||
if (errno == EINTR)
|
||||
log_error("Report interval interrupted by signal.");
|
||||
if (errno == EINVAL)
|
||||
log_error("Report interval too short.");
|
||||
r = 0;
|
||||
}
|
||||
|
||||
if (!dm_timestamp_get(ts_now)) {
|
||||
log_error("Could not obtain current timestamp.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* store interval duration in nanoseconds */
|
||||
rh->last_wait = dm_timestamp_delta(ts_now, ts_start);
|
||||
|
||||
dm_timestamp_destroy(ts_start);
|
||||
dm_timestamp_destroy(ts_now);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
1365
libdm/libdm-stats.c
Normal file
1365
libdm/libdm-stats.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -83,7 +83,7 @@ ifneq ("@THIN@", "none")
|
||||
MAN7+=lvmthin.7
|
||||
endif
|
||||
|
||||
MAN8DM=dmsetup.8 $(DMEVENTDMAN) $(BLKDEACTIVATEMAN)
|
||||
MAN8DM=dmsetup.8 dmstats.8 $(DMEVENTDMAN) $(BLKDEACTIVATEMAN)
|
||||
MAN5DIR=$(mandir)/man5
|
||||
MAN7DIR=$(mandir)/man7
|
||||
MAN8DIR=$(mandir)/man8
|
||||
|
712
man/dmstats.8.in
Normal file
712
man/dmstats.8.in
Normal file
@@ -0,0 +1,712 @@
|
||||
.TH DMSTATS 8 "Jul 25 2015" "Linux" "MAINTENANCE COMMANDS"
|
||||
.SH NAME
|
||||
dmstats \(em device-mapper statistics management
|
||||
.SH SYNOPSIS
|
||||
.ad l
|
||||
.B dmsetup stats
|
||||
.I command
|
||||
.RB [ options ]
|
||||
.br
|
||||
|
||||
.B dmstats <command>
|
||||
.RB [[
|
||||
.IR device_name ]
|
||||
.RB |[ \-\-uuid
|
||||
.IR uuid ]
|
||||
.RB |[ \-\-major
|
||||
.IR major
|
||||
.RB \-\-minor
|
||||
.IR minor ]]
|
||||
.br
|
||||
|
||||
.B dmstats clear
|
||||
.I device_name
|
||||
.RB [ \-\-allregions
|
||||
.RB | \-\-regionid
|
||||
.IR id ]
|
||||
.br
|
||||
.B dmstats create
|
||||
.I device_name
|
||||
.RB [[ \-\-areas
|
||||
.IR nr_areas ]
|
||||
.RB |[ \-\-areasize
|
||||
.IR area_size ]]
|
||||
.RB [[ \-\-start
|
||||
.IR start_sector ]
|
||||
.RB [ \-\-length
|
||||
.IR length ]
|
||||
.RB |[ \-\-targets ]]
|
||||
.RB [ \-\-auxdata
|
||||
.IR data ]
|
||||
.RB [ \-\-programid
|
||||
.IR id ]
|
||||
.br
|
||||
.B dmstats delete
|
||||
.I device_name
|
||||
.RB [ \-\-force ]
|
||||
.RB [ \-\-allregions
|
||||
.RB | \-\-regionid
|
||||
.IR id ]
|
||||
.RB [ \-\-allprograms
|
||||
.RB | \-\-programid
|
||||
.IR id ]
|
||||
.br
|
||||
.B dmstats help
|
||||
.RB [ \-c | \-C | \-\-columns ]
|
||||
.br
|
||||
.B dmstats list
|
||||
.RI [ device_name ]
|
||||
.RB [ \-\-allprograms
|
||||
.RB | \-\-programid
|
||||
.IR id ]
|
||||
.RB [ \-\-units
|
||||
.IR units ]
|
||||
.RB [ \-\-nosuffix ]
|
||||
.br
|
||||
.B dmstats print
|
||||
.RI [ device_name ]
|
||||
.RB [ \-\-clear ]
|
||||
.RB [ \-\-allprograms
|
||||
.RB | \-\-programid
|
||||
.IR id ]
|
||||
.RB [ \-\-allregions
|
||||
.RB | \-\-regionid
|
||||
.IR id ]
|
||||
.br
|
||||
.B dmstats report
|
||||
.RI [ device_name ]
|
||||
.RB [ \-\-interval
|
||||
.IR seconds ]
|
||||
.RB [ \-\-count
|
||||
.IR count ]
|
||||
.RB [ \-\-timestamps ]
|
||||
.RB [ \-\-units
|
||||
.IR units ]
|
||||
.RB [ \-\-allprograms ]
|
||||
.RB [ \-\-programid
|
||||
.IR id ]
|
||||
.RB [ \-\-headers
|
||||
.IR headers ]
|
||||
.RB [ \-\-regionid
|
||||
.IR id ]
|
||||
.RB [ \-O | \-\-sort
|
||||
.IR sort_fields ]
|
||||
.RB [ \-S | \-\-select
|
||||
.IR Selection ]
|
||||
.RB [ \-\-units
|
||||
.IR units ]
|
||||
.RB [ \-\-nosuffix ]
|
||||
.br
|
||||
.ad b
|
||||
.SH DESCRIPTION
|
||||
The dmstats program manages IO statistics regions for devices that use
|
||||
the device-mapper driver. Statistics regions may be created, deleted,
|
||||
listed and reported on using the tool.
|
||||
|
||||
The first argument to dmstats is a command.
|
||||
|
||||
The second argument is the device name, uuid, or major and minor
|
||||
numbers.
|
||||
|
||||
Further options permit the selection of regions, output format
|
||||
control, and reporting behaviour.
|
||||
|
||||
When the program is run using the 'dmstats' alias, the command
|
||||
\fBmust\fP be the first argument and any switches and options should be
|
||||
specified following the command itself. This limitation is not present
|
||||
when run as 'dmsetup stats'.
|
||||
|
||||
When no device argument is given dmstats will by default operate on all
|
||||
device-mapper devices present. The \fBcreate\fP and \fBdelete\fP
|
||||
commands require the use of \fB--force\fP when used in this way.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-\-allprograms
|
||||
Include regions from all program IDs for list and report operations.
|
||||
.TP
|
||||
.B \-\-allregions
|
||||
Include all present regions for commands that normally accept a single
|
||||
region identifier.
|
||||
.TP
|
||||
.B \-\-areas \fInr_areas
|
||||
Specify the number of statistics areas to create within a new region.
|
||||
.TP
|
||||
.B \-\-areasize \fIarea_size
|
||||
Specify the size of areas into which a new region should be divided. An
|
||||
optional suffix selects units of bBsSkKmMgGtTpPeE: (b)ytes,
|
||||
(s)ectors, (k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes,
|
||||
(p)etabytes, (e)xabytes. Capitalise to use multiples of 1000 (S.I.)
|
||||
instead of 1024.
|
||||
.TP
|
||||
.B \-\-auxdata \fIaux_data
|
||||
Specify auxilliary data (a string) to be stored with a new region.
|
||||
.TP
|
||||
.B \-\-clear
|
||||
When printing statistics counters also atomically reset them to zero.
|
||||
.TP
|
||||
.B \-\-count \fIcount
|
||||
Specify the iteration count for repeating reports. If the count
|
||||
argument is zero reports will continue to repeat until interrupted.
|
||||
.TP
|
||||
.B \-\-headers \fIheader_list
|
||||
Specify which headers to display.
|
||||
.TP
|
||||
.B \-\-interval \fIinterval
|
||||
Specify the interval, in seconds, between successive iterations for
|
||||
repeating reports.
|
||||
.TP
|
||||
.B \-\-interval \fIseconds
|
||||
Specify the interval in seconds between successive iterations for
|
||||
repeating reports. If \-\-interval is specified but \-\-count is not,
|
||||
reports will continue to repeat until interrupted.
|
||||
.TP
|
||||
.B \-\-length \fIlength
|
||||
Specify the length of a new statistics region in sectors. An optional
|
||||
suffix selects units of bBsSkKmMgGtTpPeE: (b)ytes, (s)ectors,
|
||||
(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes, (p)etabytes,
|
||||
(e)xabytes. Capitalise to use multiples of 1000 (S.I.) instead of 1024.
|
||||
.TP
|
||||
.BR \-j | \-\-major\ \fImajor
|
||||
Specify the major number.
|
||||
.TP
|
||||
.BR \-m | \-\-minor\ \fIminor
|
||||
Specify the minor number.
|
||||
.TP
|
||||
.B \-\-nosuffix
|
||||
Suppress the suffix on output sizes. Use with \fB\-\-units\fP
|
||||
(except h and H) if processing the output.
|
||||
.TP
|
||||
.BR \-o | \-\-options
|
||||
Specify which report fields to display.
|
||||
.TP
|
||||
.BR \-O | \-\-sort\ \fIsort_fields
|
||||
Sort output according to the list of fields given. Precede any
|
||||
sort_field with - for a reverse sort on that column.
|
||||
.TP
|
||||
.B \-\-programid \fIid
|
||||
Specify a program ID string. When creating new statistics regions this
|
||||
string is stored with the region. Subsequent operations may supply a
|
||||
program ID in order to select only regions with a matching value. The
|
||||
default program ID for dmstats-managed regions is "dmstats".
|
||||
.TP
|
||||
.BR \-S | \-\-select \ \fISelection
|
||||
Display only rows that match Selection criteria. All rows with the
|
||||
additional "selected" column (-o selected) showing 1 if the row matches
|
||||
the Selection and 0 otherwise. The Selection criteria are defined by
|
||||
specifying column names and their valid values while making use of
|
||||
supported comparison operators.
|
||||
.TP
|
||||
.B \-\-start \fIstart
|
||||
Specify the start offset of a new statistics region in sectors. An
|
||||
optional suffix selects units of bBsSkKmMgGtTpPeE: (b)ytes,
|
||||
(s)ectors, (k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes,
|
||||
(p)etabytes, (e)xabytes. Capitalise to use multiples of 1000 (S.I.)
|
||||
instead of 1024.
|
||||
.TP
|
||||
.B \-\-targets
|
||||
Create a new statistics region for each target contained in the target
|
||||
device. This causes a separate region to be allocated for each segment
|
||||
of the device.
|
||||
.TP
|
||||
.B \-\-timestamps
|
||||
Output a timestamp before each statistics report.
|
||||
.TP
|
||||
.BR \-\-units \ hHbBsSkKmMgGtTpPeE
|
||||
Set the display units for report output. All sizes are output in these
|
||||
units: (h)uman-readable, (b)ytes, (s)ectors, (k)ilobytes, (m)egabytes,
|
||||
(g)igabytes, (t)erabytes, (p)etabytes, (e)xabytes. Capitalise to use
|
||||
multiples of 1000 (S.I.) instead of 1024. Can also specify custom units
|
||||
e.g. \fB\-\-units 3M\fP
|
||||
.TP
|
||||
.BR \-u | \-\-uuid
|
||||
Specify the uuid.
|
||||
.TP
|
||||
.BR \-v | \-\-verbose \ [ \-v | \-\-verbose ]
|
||||
Produce additional output.
|
||||
.br
|
||||
.SH COMMANDS
|
||||
.TP
|
||||
.B clear
|
||||
.I device_name
|
||||
.RB [ \-\-allregions
|
||||
.RB | \-\-regionid
|
||||
.IR id ]
|
||||
.RB [ \-\-allprograms
|
||||
.RB | \-\-programid
|
||||
.IR id ]
|
||||
.br
|
||||
Instructs the kernel to clear statistics counters for the speficied
|
||||
regions (with the exception of in-flight IO counters).
|
||||
.br
|
||||
.TP
|
||||
.B create
|
||||
.I device_name
|
||||
.RB [ \-\-areas
|
||||
.IR nr_areas ]
|
||||
.RB [ \-\-areasize
|
||||
.IR area_size ]
|
||||
.RB [[ \-\-start
|
||||
.IR start_sector ]
|
||||
.RB [ \-\-length
|
||||
.IR length ]
|
||||
.RB |[ \-\-targets ]]
|
||||
.RB [ \-\-auxdata
|
||||
.IR data ]
|
||||
.RB [ \-\-programid
|
||||
.IR id ]
|
||||
.br
|
||||
Creates one or more new statistics regions on the specified device(s).
|
||||
|
||||
The region will span the entire device unless \fB\-\-start\fP and
|
||||
\fB\-\-length\fP or \fB\-\-target\fP are given. The \fB\-\-start\fP and
|
||||
\fB\-\-length\fP options allow a region of arbitrary length to be placed
|
||||
at an arbitrary offset into the device. The \fB\-\-targets\fP option
|
||||
causes a new region to be created for each target in the corresponding
|
||||
device-mapper device's table.
|
||||
|
||||
An optional \fBprogram_id\fP or \fBaux_data\fP string may be associated
|
||||
with the region. A \fBprogram_id\fP may then be used to select regions
|
||||
for subsequent list, print, and report operations. The \fBaux_data\fP
|
||||
stores an arbitrary string and is not used by dmstats or the
|
||||
device-mapper kernel statistics subsystem.
|
||||
|
||||
By default dmstats creates regions with a \fBprogram_id\fP of
|
||||
"DMSTATS1".
|
||||
|
||||
On success the \fBregion_id\fP of the newly created region is printed to
|
||||
stdout.
|
||||
.br
|
||||
.TP
|
||||
.B delete
|
||||
.I [ device_name ]
|
||||
.RB [ \-\-force ]
|
||||
.RB [ \-\-allregions
|
||||
.RB | \-\-regionid
|
||||
.IR id ]
|
||||
.RB [ \-\-allprograms
|
||||
.RB | \-\-programid
|
||||
.IR id ]
|
||||
.br
|
||||
Delete the specified statistics region. All counters and resources used
|
||||
by the region are released and the region will not appear in the output
|
||||
of subsequent list, print, or report operations.
|
||||
|
||||
All regions registered on a device may be removed using
|
||||
\fB\-\-allregions\fP.
|
||||
|
||||
To remove all regions on all devices \fB\-\-force\fP must be used.
|
||||
.br
|
||||
.TP
|
||||
.B help
|
||||
.RB [ \-c | \-C | \-\-columns ]
|
||||
.br
|
||||
Outputs a summary of the commands available, optionally including
|
||||
the list of report fields.
|
||||
.br
|
||||
.TP
|
||||
.B list
|
||||
.RI [ device_name ]
|
||||
.RB [ \-\-allprograms ]
|
||||
.RB [ \-\-programid
|
||||
.IR id ]
|
||||
.br
|
||||
List the statistics regions registered on the device. If the
|
||||
\fB\-\-allprograms\fP switch is given all regions will be listed
|
||||
regardless of region program ID values.
|
||||
.br
|
||||
.TP
|
||||
.B print
|
||||
.RB [ \-\-clear ]
|
||||
.IR
|
||||
.RB [ \-\-allregions
|
||||
.RB | \-\-regionid
|
||||
.IR id ]
|
||||
.RB [ \-\-allprograms
|
||||
.RB | \-\-programid
|
||||
.IR id ]
|
||||
.br
|
||||
Print raw statistics counters for the specified region or for all
|
||||
present regions.
|
||||
.br
|
||||
.TP
|
||||
.B report
|
||||
.RB [ \-\-allprograms ]
|
||||
.RB [ \-\-interval
|
||||
.IR seconds ]
|
||||
.RB [ \-\-headers
|
||||
.IR headers ]
|
||||
.RB [ \-\-count
|
||||
.IR count ]
|
||||
.RB [ \-\-timestamps ]
|
||||
.RB [ \-\-units
|
||||
.IR unit ]
|
||||
.RB [ \-\-regionid
|
||||
.IR id ]
|
||||
.RB [ \-\-programid
|
||||
.IR id ]
|
||||
.RB [ \-O | \-\-sort
|
||||
.IR sort_fields ]
|
||||
.RB [ \-S | \-\-select
|
||||
.IR Selection ]
|
||||
.RB [ \-\-units
|
||||
.IR units ]
|
||||
.br
|
||||
Start a report for the specified region or for all present regions. If
|
||||
the count argument is specified, the report will repeat at a fixed
|
||||
interval set by the \fB\-\-interval\fP option. The default interval is
|
||||
one second.
|
||||
|
||||
If the \fB\-\-allprograms\fP switch is given, all regions will be
|
||||
listed, regardless of region program ID values.
|
||||
|
||||
A header row is optionally printed before each statistics report is
|
||||
displayed. The list of headers to include is specified using the
|
||||
\fB\-\-headers\fP option.
|
||||
.br
|
||||
.SH REGIONS AND AREAS
|
||||
The device-mapper statistics facility allows separate performance
|
||||
counters to be maintained for arbitrary regions of devices. A region may
|
||||
span any range: from a single sector to the whole device. A region may
|
||||
be further sub-divided into a number of distinct areas (one or more),
|
||||
each with its own counter set.
|
||||
|
||||
By default new regions span the entire device. The \fB\-\-start\fP and
|
||||
\fB\-\-length\fP options allows a region of any size to be placed at any
|
||||
location on the device.
|
||||
|
||||
A region may be either divided into the specified number of equal-sized
|
||||
areas, or into areas of the given size by specifying one of
|
||||
\fB\-\-areas\fP or \fB\-\-areasize\fP when creating a region with the
|
||||
\fBcreate\fP command. Depending on the size of the areas and the device
|
||||
region the final area within the region may be smaller than requested.
|
||||
|
||||
.SS Region identifiers
|
||||
Each region is assigned an identifier when it is created that is used to
|
||||
reference the region in subsequent operations. Region identifiers are
|
||||
unique within a given device (including across different \fBprogram_id\fP
|
||||
values).
|
||||
.br
|
||||
Depending on the sequence of create and delete operations gaps, may
|
||||
exist in the sequence of \fBregion_id\fP values for a particular device.
|
||||
|
||||
.SH REPORT FIELDS
|
||||
The dmstats report provides several types of field that may be added to
|
||||
the default field set, or used to create custom reports.
|
||||
.br
|
||||
All performance counters and metrics are calculated per-area.
|
||||
.br
|
||||
.SS Derived metrics
|
||||
A number of metrics fields are included that provide high level
|
||||
performance indicators. These are based on the fields provided by the
|
||||
conventional Linux iostat program and are derived from the basic counter
|
||||
values provided by the kernel for each area.
|
||||
.br
|
||||
.HP
|
||||
.B rrqm
|
||||
.br
|
||||
Read requests merged per second.
|
||||
.HP
|
||||
.B wrqm
|
||||
.br
|
||||
Write requests merged per second.
|
||||
.HP
|
||||
.B rs
|
||||
.br
|
||||
Read requests per second.
|
||||
.HP
|
||||
.B ws
|
||||
.br
|
||||
Write requests per second.
|
||||
.HP
|
||||
.B rsec
|
||||
.br
|
||||
Sectors read per second.
|
||||
.HP
|
||||
.B wsec
|
||||
Sectors written per second.
|
||||
.HP
|
||||
.B arqsz
|
||||
.br
|
||||
The average size of requests submitted to the area.
|
||||
.HP
|
||||
.B qusz
|
||||
.br
|
||||
The average queue length.
|
||||
.HP
|
||||
.B await
|
||||
.br
|
||||
The average wait time for read and write requests.
|
||||
.HP
|
||||
.B r_await
|
||||
.br
|
||||
The average wait time for read requests.
|
||||
.HP
|
||||
.B w_await
|
||||
.br
|
||||
The average wait time for write requests.
|
||||
.HP
|
||||
.B tput
|
||||
.br
|
||||
The device throughput in requests per second.
|
||||
.HP
|
||||
.B svctm
|
||||
The average service time (in milliseconds) for I/O requests that
|
||||
were issued to the device.
|
||||
.HP
|
||||
.B util
|
||||
.br
|
||||
Percentage of CPU time during which I/O requests were issued to the
|
||||
device (bandwidth utilization for the device). Device saturation occurs
|
||||
when this value is close to 100%.
|
||||
.br
|
||||
.SS Region and area meta fields
|
||||
Meta fields provide information about the region or area that the
|
||||
statistics values relate to. This includes the region and area
|
||||
identifier, start, length, and counts as well as the program ID and
|
||||
auxiliary data values.
|
||||
.br
|
||||
.HP
|
||||
.B region_id
|
||||
.br
|
||||
Region identifier. This is a non-negative integer returned by the kernel
|
||||
when a statistics region is created.
|
||||
.HP
|
||||
.B region_start
|
||||
.br
|
||||
.br
|
||||
The region start sector in units of 512 byte sectors.
|
||||
.HP
|
||||
.B region_len
|
||||
.br
|
||||
The length of the region in units of 512 byte sectors.
|
||||
.HP
|
||||
.B area_id
|
||||
.br
|
||||
Area identifier. Area identifiers are assigned by the device-mapper
|
||||
statistics library and uniquely identify each area within a region. Each
|
||||
ID corresponds to a distinct set of performance counters for that area
|
||||
of the statistics region. Area identifiers are always monotonically
|
||||
increasing within a region so that higher ID values correspond to
|
||||
greater sector addresses within the region and no gaps in the sequence
|
||||
of identifiers exist. Sorting a report by device, region start, and area
|
||||
ID (the default) will then produce rows in order of ascending region and
|
||||
area address.
|
||||
.HP
|
||||
.B area_start
|
||||
.br
|
||||
The area start sector in units of 512 byte sectors.
|
||||
.HP
|
||||
.B area_len
|
||||
.br
|
||||
The length of the area in units of 512 byte sectors.
|
||||
.HP
|
||||
.B area_count
|
||||
The number of areas in this region.
|
||||
.HP
|
||||
.B program_id
|
||||
.br
|
||||
The program ID value associated with this region.
|
||||
.HP
|
||||
.B aux_data
|
||||
.br
|
||||
The auxiliary data value associated with this region.
|
||||
.br
|
||||
.SS Basic counters
|
||||
Basic counters provide access to the raw counter data from the kernel,
|
||||
allowing further processing to be carried out by another program.
|
||||
|
||||
The kernel provides thirteen separate counters for each statistics
|
||||
area. The first eleven of these match the counters provided in
|
||||
/proc/diskstats or /sys/block/*/*/stat. The final pair provide separate
|
||||
counters for read and write time.
|
||||
.P
|
||||
.HP
|
||||
.B reads
|
||||
.br
|
||||
The number of reads successfully completed this interval.
|
||||
.HP
|
||||
.B read_merges
|
||||
.br
|
||||
The number of read requests merged this interval. This field is
|
||||
incremented every time a pair of requests are merged to create a single
|
||||
request to be issued to the device.
|
||||
.HP
|
||||
.B read_sectors
|
||||
.br
|
||||
The number of 512 byte sectors read this interval.
|
||||
.HP
|
||||
.B read_nsecs
|
||||
.br
|
||||
The number of nanoseconds spent reading during this interval.
|
||||
.HP
|
||||
.B writes
|
||||
.br
|
||||
The number of writes successfully completed this interval.
|
||||
.HP
|
||||
.B write_merges
|
||||
.br
|
||||
The number of write requests merged this interval. This field is
|
||||
incremented every time a pair of requests are merged to create a single
|
||||
request to be issued to the device.
|
||||
.HP
|
||||
.B write_sectors
|
||||
.br
|
||||
The number of 512 byte sectors written this interval.
|
||||
.HP
|
||||
.B write_nsecs
|
||||
.br
|
||||
The number of nanoseconds spent writing during this interval.
|
||||
.HP
|
||||
.B in_progress
|
||||
.br
|
||||
The number of reads and writes currently in progress.
|
||||
.HP
|
||||
.B io_nsecs
|
||||
.br
|
||||
The number of nanoseconds spent reading and writing.
|
||||
.HP
|
||||
.B weighted_io_nsecs
|
||||
.br
|
||||
This field is incremented at each I/O start, I/O completion, I/O merge,
|
||||
or read of these stats by the number of I/Os in progress times the
|
||||
number of milliseconds spent doing I/O since the last update of this
|
||||
field. This can provide an easy measure of both I/O completion time and
|
||||
the backlog that may be accumulating.
|
||||
.br
|
||||
.br
|
||||
.P
|
||||
.SH EXAMPLES
|
||||
Create a whole-device region with one area on vg_hex-lv_root
|
||||
.br
|
||||
.br
|
||||
# dmstats create vg_hex-lv_root
|
||||
.br
|
||||
Created region: 0
|
||||
.br
|
||||
.br
|
||||
|
||||
|
||||
Create a 32M region 1G into device d0
|
||||
.br
|
||||
.br
|
||||
# dmstats create --start 1G --length 32M d0
|
||||
.br
|
||||
Created region: 2
|
||||
.br
|
||||
|
||||
|
||||
Create a whole-device region with 8 areas on every device
|
||||
.br
|
||||
.br
|
||||
# dmstats create --areas 8
|
||||
.br
|
||||
Created region: 0
|
||||
.br
|
||||
Created region: 0
|
||||
.br
|
||||
Created region: 0
|
||||
.br
|
||||
Created region: 2
|
||||
.br
|
||||
Created region: 0
|
||||
.br
|
||||
Created region: 0
|
||||
.br
|
||||
.br
|
||||
|
||||
Delete all regions on all devices
|
||||
.br
|
||||
.br
|
||||
# dmstats delete --allregions --force
|
||||
.br
|
||||
.br
|
||||
|
||||
Create a whole-device region with areas 10GiB in size on vg_hex-lv_root
|
||||
using dmsetup
|
||||
.br
|
||||
.br
|
||||
# dmsetup stats create --areasize 10G vg_hex-lv_root
|
||||
.br
|
||||
Created region: 1
|
||||
.br
|
||||
.br
|
||||
|
||||
Create a 1GiB region with 16 areas at the start of vg_hex-lv_root
|
||||
.br
|
||||
# dmstats create --start 0 --len 1G --areas=16 vg_hex-lv_root
|
||||
.br
|
||||
Created region: 2
|
||||
.br
|
||||
.br
|
||||
|
||||
List the statistics regions registered on vg_hex-lv_root
|
||||
.br
|
||||
# dmstats list vg_hex-lv_root
|
||||
.br
|
||||
RegionID RegStart RegLen AreaSize ProgramID AuxData
|
||||
.br
|
||||
0 0 104857600 20971520 dmstats
|
||||
.br
|
||||
1 0 104857600 20971520 dmstats
|
||||
.br
|
||||
2 0 2097152 131072 dmstats
|
||||
.br
|
||||
.br
|
||||
|
||||
Display five statistics reports, with timestamps, for vg_hex-lv_root at an interval of one second
|
||||
.br
|
||||
.br
|
||||
# dmstats report --time --interval 1 --count 5
|
||||
.br
|
||||
21/07/15 21:04:26
|
||||
.br
|
||||
Name RgID ArID RRqM/s WRqM/s R/s W/s RSz/s WSz/s AvRqSz QSize SvcTm Util% AWait
|
||||
.br
|
||||
vg_hex-lv_root 0 0 0.00 0.00 8.00 0.00 48.00k 0 6.00k 0.00 5.50 4.40 6.62
|
||||
.br
|
||||
vg_hex-lv_root 0 1 0.00 0.00 22.00 0.00 624.00k 0 28.00k 0.00 5.23 11.50 5.36
|
||||
.br
|
||||
vg_hex-lv_root 0 2 0.00 0.00 353.00 0.00 1.84m 0 5.00k 0.00 1.34 47.40 1.33
|
||||
.br
|
||||
vg_hex-lv_root 0 3 0.00 0.00 73.00 0.00 592.00k 0 8.00k 0.00 2.10 15.30 2.10
|
||||
.br
|
||||
vg_hex-lv_root 0 4 0.00 0.00 5.00 0.00 52.00k 0 10.00k 0.00 4.00 2.00 4.00
|
||||
.br
|
||||
[...]
|
||||
.br
|
||||
.br
|
||||
|
||||
Create one region for reach target contained in device vg_hex-lv_home
|
||||
.br
|
||||
.br
|
||||
# dmstats create --targets vg_hex-lv_home
|
||||
.br
|
||||
Created region: 0
|
||||
.br
|
||||
Created region: 1
|
||||
.br
|
||||
Created region: 2
|
||||
.br
|
||||
.br
|
||||
|
||||
Print raw counters for region 4 on device d0
|
||||
.br
|
||||
.br
|
||||
# dmstats print --regionid 4 d0
|
||||
.br
|
||||
2097152+65536 0 0 0 0 29 0 264 701 0 41 701 0 41
|
||||
.br
|
||||
.br
|
||||
.SH AUTHORS
|
||||
Bryn M. Reeves <bmr@redhat.com>
|
||||
|
||||
.SH SEE ALSO
|
||||
LVM2 resource page https://www.sourceware.org/lvm2/
|
||||
.br
|
||||
Device-mapper resource page: http://sources.redhat.com/dm/
|
||||
.br
|
||||
|
||||
Device-mapper statistics kernel documentation
|
||||
.br
|
||||
Documentation/device-mapper/statistics.txt
|
1936
tools/dmsetup.c
1936
tools/dmsetup.c
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user