From 7842c5564f4636368fd54e8422ca1e2da2d30a2a Mon Sep 17 00:00:00 2001 From: Sebastien Godard Date: Tue, 24 May 2011 09:24:21 +0200 Subject: [PATCH] Option -m USB added to sar (sadc POWER group) This option takes a snapshot of current USB devices plugged into the system. Idea from ceolin@ulevel.com (Fabricio Ceolin). --- activity.c | 33 +++++++++++- common.h | 6 +++ pr_stats.c | 112 ++++++++++++++++++++++++++++++++++++++ pr_stats.h | 4 ++ rd_stats.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++ rd_stats.h | 22 ++++++++ rndr_stats.c | 24 +++++++-- rndr_stats.h | 2 + sa.h | 11 +++- sa_common.c | 4 ++ sa_wrap.c | 46 +++++++++++++++- sar.c | 3 +- xml_stats.c | 17 ++++++ xml_stats.h | 2 + 14 files changed, 427 insertions(+), 8 deletions(-) diff --git a/activity.c b/activity.c index 7f276c4..ed0666f 100644 --- a/activity.c +++ b/activity.c @@ -1127,6 +1127,36 @@ struct activity pwr_wghfreq_act = { .bitmap = &cpu_bitmap }; +/* USB devices plugged into the system */ +struct activity pwr_usb_act = { + .id = A_PWR_USB, + .options = AO_NULL, + .magic = ACTIVITY_MAGIC_BASE, + .group = G_POWER, +#ifdef SOURCE_SADC + .f_count = wrap_get_usb_nr, + .f_count2 = NULL, + .f_read = wrap_read_bus_usb_dev, +#endif +#ifdef SOURCE_SAR + .f_print = print_pwr_usb_stats, + .f_print_avg = print_avg_pwr_usb_stats, +#endif +#ifdef SOURCE_SADF + .f_render = render_pwr_usb_stats, + .f_xml_print = xml_print_pwr_usb_stats, + .hdr_line = "manufact;product;BUS;idvendor;idprod;maxpower", + .name = "A_PWR_USB", +#endif + .nr = -1, + .nr2 = 1, + .fsize = STATS_PWR_USB_SIZE, + .msize = STATS_PWR_USB_SIZE, + .opt_flags = 0, + .buf = {NULL, NULL, NULL}, + .bitmap = NULL +}; + /* * Array of activities. @@ -1166,5 +1196,6 @@ struct activity *act[NR_ACT] = { &pwr_temp_act, &pwr_in_act, &huge_act, - &pwr_wghfreq_act + &pwr_wghfreq_act, + &pwr_usb_act }; diff --git a/common.h b/common.h index 1fb225c..3eff7ed 100644 --- a/common.h +++ b/common.h @@ -56,6 +56,12 @@ #define S_STAT "stat" #define DEVMAP_DIR "/dev/mapper" #define DEVICES "/proc/devices" +#define SYSFS_USBDEV "/sys/bus/usb/devices" +#define SYSFS_IDVENDOR "idVendor" +#define SYSFS_IDPRODUCT "idProduct" +#define SYSFS_BMAXPOWER "bMaxPower" +#define SYSFS_MANUFACTURER "manufacturer" +#define SYSFS_PRODUCT "product" #define MAX_FILE_LEN 256 #define MAX_PF_NAME 1024 diff --git a/pr_stats.c b/pr_stats.c index 241b5a0..44ac00a 100644 --- a/pr_stats.c +++ b/pr_stats.c @@ -2311,3 +2311,115 @@ void print_pwr_wghfreq_stats(struct activity *a, int prev, int curr, } } } + +/* + *************************************************************************** + * Display USB devices statistics. This function is used to + * display instantaneous and summary statistics. + * + * IN: + * @a Activity structure with statistics. + * @curr Index in array for current sample statistics. + * @itv Interval of time in jiffies. + * @dispavg TRUE if displaying average statistics. + *************************************************************************** + */ +void stub_print_pwr_usb_stats(struct activity *a, int curr, int dispavg) +{ + int i, j; + struct stats_pwr_usb *suc, *sum; + + if (dis) { + printf("\n%-11s BUS idvendor idprod maxpower", + (dispavg ? _("Summary") : timestamp[!curr])); + printf(" %*s", MAX_MANUF_LEN - 1, "manufact"); + printf(" %*s\n", MAX_PROD_LEN - 1, "product"); + } + + for (i = 0; i < a->nr; i++) { + suc = (struct stats_pwr_usb *) ((char *) a->buf[curr] + i * a->msize); + + if (!suc->bus_nr) + /* Bus#0 doesn't exist: We are at the end of the list */ + break; + + printf("%-11s %6d %9x %9x %9u", + (dispavg ? _("Summary") : timestamp[curr]), + suc->bus_nr, + suc->vendor_id, suc->product_id, + /* bMaxPower is expressed in 2 mA units */ + suc->bmaxpower << 1); + + printf(" %*s", MAX_MANUF_LEN - 1, suc->manufacturer); + printf(" %*s\n", MAX_PROD_LEN - 1, suc->product); + + if (!dispavg) { + /* Save current USB device in summary list */ + for (j = 0; j < a->nr; j++) { + sum = (struct stats_pwr_usb *) ((char *) a->buf[2] + j * a->msize); + + if ((sum->bus_nr == suc->bus_nr) && + (sum->vendor_id == suc->vendor_id) && + (sum->product_id == suc->product_id)) + /* USB device found in summary list */ + break; + if (!sum->bus_nr) { + /* + * Current slot is free: + * Save USB device in summary list. + */ + *sum = *suc; + break; + } + if (j == a->nr - 1) { + /* + * This is the last slot and + * we still havent't found the device. + * So create a dummy device here. + */ + sum->bus_nr = ~0; + sum->vendor_id = sum->product_id = 0; + sum->bmaxpower = 0; + sum->manufacturer[0] = '\0'; + snprintf(sum->product, MAX_PROD_LEN, "%s", + _("Other devices not listed here")); + sum->product[MAX_PROD_LEN - 1] = '\0'; + } + } + } + } +} + +/* + *************************************************************************** + * Display memory and swap statistics. + * + * IN: + * @a Activity structure with statistics. + * @prev Index in array where stats used as reference are. + * @curr Index in array for current sample statistics. + * @itv Interval of time in jiffies. + *************************************************************************** + */ +__print_funct_t print_pwr_usb_stats(struct activity *a, int prev, int curr, + unsigned long long itv) +{ + stub_print_pwr_usb_stats(a, curr, FALSE); +} + +/* + *************************************************************************** + * Display average memory statistics. + * + * IN: + * @a Activity structure with statistics. + * @prev Index in array where stats used as reference are. + * @curr Index in array for current sample statistics. + * @itv Interval of time in jiffies. + *************************************************************************** + */ +__print_funct_t print_avg_pwr_usb_stats(struct activity *a, int prev, int curr, + unsigned long long itv) +{ + stub_print_pwr_usb_stats(a, 2, TRUE); +} diff --git a/pr_stats.h b/pr_stats.h index 5228cfd..0f54aab 100644 --- a/pr_stats.h +++ b/pr_stats.h @@ -86,6 +86,8 @@ extern __print_funct_t print_huge_stats (struct activity *, int, int, unsigned long long); extern __print_funct_t print_pwr_wghfreq_stats (struct activity *, int, int, unsigned long long); +extern __print_funct_t print_pwr_usb_stats + (struct activity *, int, int, unsigned long long); /* Functions used to display average statistics */ extern __print_funct_t print_avg_memory_stats @@ -108,5 +110,7 @@ extern __print_funct_t print_avg_pwr_in_stats (struct activity *, int, int, unsigned long long); extern __print_funct_t print_avg_huge_stats (struct activity *, int, int, unsigned long long); +extern __print_funct_t print_avg_pwr_usb_stats + (struct activity *, int, int, unsigned long long); #endif /* _PR_STATS_H */ diff --git a/rd_stats.c b/rd_stats.c index 41a511c..13ad0c1 100644 --- a/rd_stats.c +++ b/rd_stats.c @@ -1742,6 +1742,121 @@ void read_time_in_state(struct stats_pwr_wghfreq *st_pwr_wghfreq, int cpu_nr, in fclose(fp); } +/* + *************************************************************************** + * Read current USB device data. + * + * IN: + * @st_pwr_usb Structure where stats will be saved. + * @usb_device File name for current USB device. + * + * OUT: + * @st_pwr_usb Structure with statistics. + *************************************************************************** + */ +void read_usb_stats(struct stats_pwr_usb *st_pwr_usb, char *usb_device) +{ + int l; + FILE *fp; + char filename[MAX_PF_NAME]; + + /* Get USB device bus number */ + sscanf(usb_device, "%u", &st_pwr_usb->bus_nr); + + /* Read USB device vendor ID */ + snprintf(filename, MAX_PF_NAME, "%s/%s/%s", + SYSFS_USBDEV, usb_device, SYSFS_IDVENDOR); + if ((fp = fopen(filename, "r")) != NULL) { + fscanf(fp, "%x", + &st_pwr_usb->vendor_id); + fclose(fp); + } + + /* Read USB device product ID */ + snprintf(filename, MAX_PF_NAME, "%s/%s/%s", + SYSFS_USBDEV, usb_device, SYSFS_IDPRODUCT); + if ((fp = fopen(filename, "r")) != NULL) { + fscanf(fp, "%x", + &st_pwr_usb->product_id); + fclose(fp); + } + + /* Read USB device max power consumption */ + snprintf(filename, MAX_PF_NAME, "%s/%s/%s", + SYSFS_USBDEV, usb_device, SYSFS_BMAXPOWER); + if ((fp = fopen(filename, "r")) != NULL) { + fscanf(fp, "%u", + &st_pwr_usb->bmaxpower); + fclose(fp); + } + + /* Read USB device manufacturer */ + snprintf(filename, MAX_PF_NAME, "%s/%s/%s", + SYSFS_USBDEV, usb_device, SYSFS_MANUFACTURER); + if ((fp = fopen(filename, "r")) != NULL) { + fgets(st_pwr_usb->manufacturer, MAX_MANUF_LEN - 1, fp); + fclose(fp); + if ((l = strlen(st_pwr_usb->manufacturer)) > 0) { + /* Remove trailing CR */ + st_pwr_usb->manufacturer[l - 1] = '\0'; + } + } + + /* Read USB device product */ + snprintf(filename, MAX_PF_NAME, "%s/%s/%s", + SYSFS_USBDEV, usb_device, SYSFS_PRODUCT); + if ((fp = fopen(filename, "r")) != NULL) { + fgets(st_pwr_usb->product, MAX_PROD_LEN - 1, fp); + fclose(fp); + if ((l = strlen(st_pwr_usb->product)) > 0) { + /* Remove trailing CR */ + st_pwr_usb->product[l - 1] = '\0'; + } + } +} + +/* + *************************************************************************** + * Read USB devices statistics. + * + * IN: + * @st_pwr_usb Structure where stats will be saved. + * @nbr Total number of USB devices. + * + * OUT: + * @st_pwr_usb Structure with statistics. + *************************************************************************** + */ +void read_bus_usb_dev(struct stats_pwr_usb *st_pwr_usb, int nbr) +{ + DIR *dir; + struct dirent *drd; + struct stats_pwr_usb *st_pwr_usb_j; + int j = 0; + + /* Open relevant /sys directory */ + if ((dir = opendir(SYSFS_USBDEV)) == NULL) + return; + + /* Get current file entry */ + while ((drd = readdir(dir)) != NULL) { + + if (isdigit(drd->d_name[0]) && !strchr(drd->d_name, ':')) { + if (j < nbr) { + /* Read current USB device data */ + st_pwr_usb_j = st_pwr_usb + j; + read_usb_stats(st_pwr_usb_j, drd->d_name); + j++; + } + else + break; + } + } + + /* Close directory */ + closedir(dir); +} + /* *************************************************************************** * Read machine uptime, independently of the number of processors. @@ -2127,3 +2242,37 @@ int get_freq_nr(void) return freq; } + +/* + *************************************************************************** + * Count number of USB devices in /sys/bus/usb/devices. + * + * RETURNS: + * Number of USB devices plugged into the system. + * Don't count USB root hubs. + * Return -1 if directory doesn't exist in sysfs. + *************************************************************************** + */ +int get_usb_nr(void) +{ + DIR *dir; + struct dirent *drd; + int usb = 0; + + /* Open relevant /sys directory */ + if ((dir = opendir(SYSFS_USBDEV)) == NULL) + return -1; + + /* Get current file entry */ + while ((drd = readdir(dir)) != NULL) { + + if (isdigit(drd->d_name[0]) && !strchr(drd->d_name, ':')) { + usb++; + } + } + + /* Close directory */ + closedir(dir); + + return usb; +} diff --git a/rd_stats.h b/rd_stats.h index 9b01ac1..23f854f 100644 --- a/rd_stats.h +++ b/rd_stats.h @@ -23,6 +23,10 @@ /* Maximum length of network interface name */ #define MAX_IFACE_LEN IFNAMSIZ +/* Maximum length of USB manufacturer string */ +#define MAX_MANUF_LEN 24 +/* Maximum length of USB product string */ +#define MAX_PROD_LEN 48 #define CNT_DEV 0 #define CNT_PART 1 @@ -504,6 +508,20 @@ struct stats_pwr_wghfreq { #define STATS_PWR_WGHFREQ_SIZE (sizeof(struct stats_pwr_wghfreq)) +/* + * Structure for USB devices plugged into the system. + */ +struct stats_pwr_usb { + unsigned int bus_nr __attribute__ ((aligned (4))); + unsigned int vendor_id __attribute__ ((packed)); + unsigned int product_id __attribute__ ((packed)); + unsigned int bmaxpower __attribute__ ((packed)); + char manufacturer[MAX_MANUF_LEN]; + char product[MAX_PROD_LEN]; +}; + +#define STATS_PWR_USB_SIZE (sizeof(struct stats_pwr_usb)) + /* *************************************************************************** * Prototypes for functions used to read system statistics @@ -577,6 +595,8 @@ extern void read_meminfo_huge(struct stats_huge *); extern void read_time_in_state(struct stats_pwr_wghfreq *, int, int); +extern void + read_bus_usb_dev(struct stats_pwr_usb *, int); /* *************************************************************************** @@ -600,5 +620,7 @@ extern int get_irqcpu_nr(char *, int, int); extern int get_freq_nr(void); +extern int + get_usb_nr(void); #endif /* _RD_STATS_H */ diff --git a/rndr_stats.c b/rndr_stats.c index 1265bcc..6b628e7 100644 --- a/rndr_stats.c +++ b/rndr_stats.c @@ -55,7 +55,7 @@ extern unsigned int dm_major; * does: load a static Cons with values using the t parameter to * guide pulling values from the arglist * - * return: the address of it's static Cons. If you need to keep + * return: the address of its static Cons. If you need to keep * the contents of this Cons, copy it somewhere before calling * cons() against to avoid overwrite. * ie. don't do this: f( cons( iv, i, j ), cons( iv, a, b ) ); @@ -2298,7 +2298,6 @@ __print_funct_t render_pwr_temp_stats(struct activity *a, int isdb, char *pre, (spc->temp_max - spc->temp_min) ? (spc->temp - spc->temp_min) / (spc->temp_max - spc->temp_min) * 100 : 0.0); - } else { render(isdb, pre, PT_NOFLAG, @@ -2358,7 +2357,6 @@ __print_funct_t render_pwr_in_stats(struct activity *a, int isdb, char *pre, (spc->in_max - spc->in_min) ? (spc->in - spc->in_min) / (spc->in_max - spc->in_min) * 100 : 0.0); - } else { render(isdb, pre, PT_NOFLAG, @@ -2475,3 +2473,23 @@ __print_funct_t render_pwr_wghfreq_stats(struct activity *a, int isdb, char *pre } } } + +/* + *************************************************************************** + * Display USB devices statistics in selected format. + * + * IN: + * @a Activity structure with statistics. + * @isdb Flag, true if db printing, false if ppc printing. + * @pre Prefix string for output entries + * @curr Index in array for current sample statistics. + * @itv Interval of time in jiffies. + *************************************************************************** + */ +__print_funct_t render_pwr_usb_stats(struct activity *a, int isdb, char *pre, + int curr, unsigned long long itv) +{ +/* +FIXME +*/ +} diff --git a/rndr_stats.h b/rndr_stats.h index 9a1124c..41ef4a0 100644 --- a/rndr_stats.h +++ b/rndr_stats.h @@ -114,5 +114,7 @@ extern __print_funct_t render_huge_stats (struct activity *, int, char *, int, unsigned long long); extern __print_funct_t render_pwr_wghfreq_stats (struct activity *, int, char *, int, unsigned long long); +extern __print_funct_t render_pwr_usb_stats + (struct activity *, int, char *, int, unsigned long long); #endif /* _RNDR_STATS_H */ diff --git a/sa.h b/sa.h index 57021dd..cfd0486 100644 --- a/sa.h +++ b/sa.h @@ -17,7 +17,7 @@ */ /* Number of activities */ -#define NR_ACT 35 +#define NR_ACT 36 /* Activities */ #define A_CPU 1 @@ -55,6 +55,7 @@ #define A_PWR_IN 33 #define A_HUGE 34 #define A_PWR_WGHFREQ 35 +#define A_PWR_USB 36 /* Macro used to flag an activity that should be collected */ @@ -158,6 +159,7 @@ #define K_SNMP "SNMP" #define K_IPV6 "IPV6" #define K_POWER "POWER" +#define K_USB "USB" /* Groups of activities */ #define G_DEFAULT 0x00 @@ -186,6 +188,7 @@ #define NR_SERIAL_PREALLOC 2 #define NR_DISK_PREALLOC 3 #define NR_FREQ_PREALLOC 0 +#define NR_USB_PREALLOC 5 #define UTSNAME_LEN 65 #define TIMESTAMP_LEN 16 @@ -687,7 +690,9 @@ extern __nr_t wrap_get_in_nr(struct activity *); extern __nr_t wrap_get_freq_nr(struct activity *); - +extern __nr_t + wrap_get_usb_nr(struct activity *); + /* Functions used to read activities statistics */ extern __read_funct_t wrap_read_stat_cpu(struct activity *); @@ -759,6 +764,8 @@ extern __read_funct_t wrap_read_meminfo_huge(struct activity *); extern __read_funct_t wrap_read_time_in_state(struct activity *); +extern __read_funct_t + wrap_read_bus_usb_dev(struct activity *); /* Other functions */ extern void diff --git a/sa_common.c b/sa_common.c index 0d318b2..4297087 100644 --- a/sa_common.c +++ b/sa_common.c @@ -1348,12 +1348,16 @@ int parse_sar_m_opt(char *argv[], int *opt, struct activity *act[]) else if (!strcmp(t, K_FREQ)) { SELECT_ACTIVITY(A_PWR_WGHFREQ); } + else if (!strcmp(t, K_USB)) { + SELECT_ACTIVITY(A_PWR_USB); + } else if (!strcmp(t, K_ALL)) { SELECT_ACTIVITY(A_PWR_CPUFREQ); SELECT_ACTIVITY(A_PWR_FAN); SELECT_ACTIVITY(A_PWR_IN); SELECT_ACTIVITY(A_PWR_TEMP); SELECT_ACTIVITY(A_PWR_WGHFREQ); + SELECT_ACTIVITY(A_PWR_USB); } else return 1; diff --git a/sa_wrap.c b/sa_wrap.c index 3fd08ef..321b227 100644 --- a/sa_wrap.c +++ b/sa_wrap.c @@ -825,6 +825,28 @@ __read_funct_t wrap_read_time_in_state(struct activity *a) return; } +/* + *************************************************************************** + * Read USB devices statistics. + * + * IN: + * @a Activity structure. + * + * OUT: + * @a Activity structure with statistics. + *************************************************************************** + */ +__read_funct_t wrap_read_bus_usb_dev(struct activity *a) +{ + struct stats_pwr_usb *st_pwr_usb + = (struct stats_pwr_usb *) a->_buf0; + + /* Read USB devices stats */ + read_bus_usb_dev(st_pwr_usb, a->nr); + + return; +} + /* *************************************************************************** * Count number of interrupts that are in /proc/stat file. @@ -990,7 +1012,7 @@ __nr_t wrap_get_in_nr(struct activity *a) * Count number of possible frequencies for CPU#0. * * IN: - * @a Activity structure. + * @a Activity structure. * * RETURNS: * Number of CPU frequencies + a pre-allocation constant. @@ -1005,3 +1027,25 @@ __nr_t wrap_get_freq_nr(struct activity *a) return 0; } + +/* + *************************************************************************** + * Count number of USB devices plugged into the system. + * + * IN: + * @a Activity structure. + * + * RETURNS: + * Number of USB devices + a pre-allocation constant. + *************************************************************************** + */ +__nr_t wrap_get_usb_nr(struct activity *a) +{ + __nr_t n = 0; + + if ((n = get_usb_nr()) >= 0) + /* Return a positive number even if no USB devices have been found */ + return (n + NR_USB_PREALLOC); + + return 0; +} diff --git a/sar.c b/sar.c index 3597167..75df408 100644 --- a/sar.c +++ b/sar.c @@ -137,7 +137,8 @@ void display_help(char *progname) "\t\tFAN\tFans speed\n" "\t\tFREQ\tCPU average clock frequency\n" "\t\tIN\tVoltage inputs\n" - "\t\tTEMP\tDevices temperature\n")); + "\t\tTEMP\tDevices temperature\n" + "\t\tUSB\tUSB devices plugged into the system\n")); printf(_("\t-n { [,...] | ALL }\n" "\t\tNetwork statistics\n" "\t\tKeywords are:\n" diff --git a/xml_stats.c b/xml_stats.c index 105660e..ac108f5 100644 --- a/xml_stats.c +++ b/xml_stats.c @@ -1957,3 +1957,20 @@ close_xml_markup: xml_markup_power_management(tab, CLOSE_XML_MARKUP); } } + +/* + *************************************************************************** + * Display USB devices statistics in XML. + * + * IN: + * @a Activity structure with statistics. + * @curr Index in array for current sample statistics. + * @tab Indentation in XML output. + * @itv Interval of time in jiffies. + *************************************************************************** + */ +__print_funct_t xml_print_pwr_usb_stats(struct activity *a, int curr, int tab, + unsigned long long itv) +{ + /* FIXME */ +} \ No newline at end of file diff --git a/xml_stats.h b/xml_stats.h index 385ab43..bc1b38c 100644 --- a/xml_stats.h +++ b/xml_stats.h @@ -87,5 +87,7 @@ extern __print_funct_t xml_print_huge_stats (struct activity *, int, int, unsigned long long); extern __print_funct_t xml_print_pwr_wghfreq_stats (struct activity *, int, int, unsigned long long); +extern __print_funct_t xml_print_pwr_usb_stats + (struct activity *, int, int, unsigned long long); #endif /* _XML_STATS_H */ -- 2.50.1