[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v3 3/3] xentrace: Implement cpu mask range parsing of human values (-c).



On 03/24/2015 03:39 PM, Konrad Rzeszutek Wilk wrote:
> Instead of just using -c 0x<some hex value> we can
> also use -c <starting cpu>-<end cpu> or -c <cpu1>,<cpu2>
> or a combination of them. Also it can include just
> singular CPUs: -c <cpu1>, or ranges without an
> start or end (and xentrace will figure out the values), such
> as: -c -<cpu2> (which will include cpu0, cpu1, and cpu2) or
> -c <cpu2>- (which will include cpu2 and up to MAX_CPUS).
> 
> That should make it easier to trace the right CPU if
> using this along with 'xl vcpu-list'.
> 
> The code has been lifted from the Linux kernel, see file
> lib/bitmap.c, function __bitmap_parselist.
> 
> To make the old behavior and the new function work, we check
> to see if the arguments have '0x' in them. If they do
> we use the old style parsing (limited to 32 CPUs). If that
> does not exist we use the new parsing.
> 
> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx>
> [v4: Fix per George's review]
> ---
>  tools/xentrace/xentrace.8 |  34 ++++++++-
>  tools/xentrace/xentrace.c | 190 
> ++++++++++++++++++++++++++++++++++++++--------
>  2 files changed, 188 insertions(+), 36 deletions(-)
> 
> diff --git a/tools/xentrace/xentrace.8 b/tools/xentrace/xentrace.8
> index c176a96..eb6fba8 100644
> --- a/tools/xentrace/xentrace.8
> +++ b/tools/xentrace/xentrace.8
> @@ -36,10 +36,36 @@ all new records to the output
>  set the time, p, (in milliseconds) to sleep between polling the buffers
>  for new data.
>  .TP
> -.B -c, --cpu-mask=c
> -set bitmask of CPUs to trace. It is limited to 32-bits.
> -If not specified, the cpu-mask of all of the available CPUs will be
> -constructed.
> +.B -c, --cpu-mask=[\fIc\fP|\fICPU-LIST\fP]
> +This can either be a hex value (of the form 0xNNNN...), or a set of cpu
> +ranges as described below. Hex values are limited to 32 bits. If not
> +specified, the cpu-mask of all of the available CPUs will be
> +constructed. If using the \fICPU-LIST\fP it expects decimal numbers, which
> +may be specified as follows:
> +
> +.RS 4
> +.ie n .IP """0-3""" 4
> +.el .IP "``0-3''" 4
> +.IX Item "0-3"
> +Trace only on CPUs 0 through 3
> +.ie n .IP """0,2,5-7""" 4
> +.el .IP "``0,2,5-7''" 4
> +.IX Item "0,2,5-7"
> +Trace only on CPUs 0, 2, and 5 through 7.
> +.ie n .IP """-3""" 4
> +.el .IP "``-3''" 4
> +.IX Item "-3"
> +Trace only on CPUs 0 through 3
> +.ie n .IP """-3,7""" 4
> +.el .IP "``-3,7''" 4
> +.IX Item "-3,7"
> +Trace only on CPUs 0 through 3 and 7
> +.ie n .IP """3-""" 4
> +.el .IP "``3-''" 4
> +.IX Item "-3-"
> +Trace only on CPUs 3 up to maximum numbers of CPUs the host has.
> +.RE
> +.Sp
>  
>  .TP
>  .B -e, --evt-mask=e
> diff --git a/tools/xentrace/xentrace.c b/tools/xentrace/xentrace.c
> index 40504ec..3dd5c01 100644
> --- a/tools/xentrace/xentrace.c
> +++ b/tools/xentrace/xentrace.c
> @@ -23,6 +23,7 @@
>  #include <string.h>
>  #include <getopt.h>
>  #include <assert.h>
> +#include <ctype.h>
>  #include <sys/poll.h>
>  #include <sys/statvfs.h>
>  
> @@ -54,6 +55,7 @@ typedef struct settings_st {
>      uint32_t evt_mask;
>      xc_cpumap_t cpu_mask;
>      int cpu_bits;
> +    char *cpu_mask_str;
>      unsigned long tbuf_size;
>      unsigned long disk_rsvd;
>      unsigned long timeout;
> @@ -545,25 +547,6 @@ void print_cpu_mask(xc_cpumap_t mask, int bits)
>  
>  static void set_cpu_mask(xc_cpumap_t mask, int bits)
>  {
> -    int i;
> -
> -    if ( bits <= 0 )
> -    {
> -        bits = xc_get_max_cpus(xc_handle);
> -        if ( bits <= 0 )
> -            goto out;
> -    }
> -    if ( !mask )
> -    {
> -        mask = xc_cpumap_alloc(xc_handle);
> -        if ( !mask )
> -            goto out;
> -
> -        /* Set it to include _all_ CPUs. */
> -        for ( i = 0; i < XC_DIV_ROUND_UP(bits, 8); i++ )
> -            mask[i] = 0xff;
> -    }
> -    /* And this will limit it to the exact amount of bits. */
>      if ( xc_tbuf_set_cpu_mask(xc_handle, mask, bits) )
>          goto out;
>  
> @@ -822,7 +805,7 @@ static void usage(void)
>  "Usage: xentrace [OPTION...] [output file]\n" \
>  "Tool to capture Xen trace buffer data\n" \
>  "\n" \
> -"  -c, --cpu-mask=c        Set cpu-mask\n" \
> +"  -c, --cpu-mask=c        Set cpu-mask, using either hex or CPU ranges\n" \
>  "  -e, --evt-mask=e        Set evt-mask\n" \
>  "  -s, --poll-sleep=p      Set sleep time, p, in milliseconds between\n" \
>  "                          polling the trace buffer for new data\n" \
> @@ -976,6 +959,156 @@ static int parse_cpumask(const char *arg)
>      return 0;
>  }
>  
> +#define ZERO_DIGIT '0'
> +
> +static int parse_cpumask_range(const char *arg)
> +{
> +    xc_cpumap_t map;
> +    unsigned int a, b, buflen = strlen(arg);
> +    int c, c_old, totaldigits, nmaskbits;
> +    int in_range;
> +    const char *s;
> +
> +    if ( !buflen )
> +    {
> +        fprintf(stderr, "Invalid option argument: %s\n", arg);
> +        errno = EINVAL;
> +        return EXIT_FAILURE;
> +    }

I think we need blank lines between these if() statements

> +    nmaskbits = xc_get_max_cpus(xc_handle);

[space]

> +    if ( nmaskbits <= 0 )
> +    {
> +        fprintf(stderr, "Failed to get max number of CPUs! rc: %d\n", 
> nmaskbits);
> +        return EXIT_FAILURE;
> +    }

[space]

> +    map = xc_cpumap_alloc(xc_handle);

[space] &c

> +    if ( !map )
> +    {
> +        fprintf(stderr, "Out of memory!\n");
> +        return EXIT_FAILURE;
> +    }
> +    c = c_old = totaldigits = 0;
> +    s = arg;
> +    do {
> +        in_range = 0;
> +        a = b = 0;
> +        /* The buflen can become zero in the '} while(..) below. */
> +        while ( buflen )
> +        {
> +            c_old = c;
> +            c = *s++;
> +            buflen--;
> +
> +            if ( isspace(c) )
> +                continue;
> +
> +            if ( totaldigits && c && isspace(c_old) )
> +            {
> +                fprintf(stderr, "No embedded whitespaces allowed in: %s\n", 
> arg);
> +                goto err_out;
> +            }

Why are we even bothering to handle spaces here?  Who would put
  xentrace -c "    0-3,5" /tmp/foo
and why would we want to accept that input?

Not handling initial whitespace lets us get rid of totaldigits and c_old
as well.

> +
> +            /* A '\0' or a ',' signal the end of a cpu# or range */
> +            if ( c == '\0' || c == ',' )
> +                break;

Can c=='\0' ever be true here?  Isn't that why we do all the accounting
w/ buflen?

[snip]

> +
> +/**
> + * Figure out which of the CPU types the user has provided - either the hex
> + * variant or the cpu-list. Once done set the CPU mask.
> + */
> +static int figure_cpu_mask(void)
> +{
> +    int ret = EXIT_FAILURE;
> +
> +    if ( opts.cpu_mask_str )

I think we should move this if into main(), similar to opts.evt_mask.

> +    {
> +        if ( strlen(opts.cpu_mask_str) < 1 )
> +        {
> +            errno = ENOSPC;
> +            goto out;
> +        }
> +        if ( strncmp("0x", opts.cpu_mask_str, 2) == 0 )
> +            ret = parse_cpumask(opts.cpu_mask_str);
> +        else
> +            ret = parse_cpumask_range(opts.cpu_mask_str);

Would it make sense to add an "all" option here?

Also -- I think it might make sense to allocate the cpumask in this
function, and then have parse_cpumask* set the bits it wants.  Then we
have a nice symmetric alloc and free.

> +    }
> +    else

And we can get rid of this else.

> +    {
> +        int i, bits;
> +        xc_cpumap_t mask;
> +
> +        bits = xc_get_max_cpus(xc_handle);
> +        if ( bits <= 0 )
> +            goto out;
> +
> +        mask = xc_cpumap_alloc(xc_handle);
> +        if ( !mask )
> +            goto out;
> +
> +        /* Set it to include _all_ CPUs. */
> +        for ( i = 0; i < XC_DIV_ROUND_UP(bits, 8); i++ )
> +            mask[i] = 0xff;
> +
> +        opts.cpu_mask = mask;
> +        opts.cpu_bits = bits;
> +        ret = 0;
> +    }
> +    if ( ret != EXIT_FAILURE )
> +        set_cpu_mask(opts.cpu_mask, opts.cpu_bits);
> + out:
> +    /* We don't use it pass this point. */
> +    free(opts.cpu_mask_str);

I guess we also want to free opts.cpu_mask

> +    return ret;
> +}
> +
>  /* parse command line arguments */
>  static void parse_args(int argc, char **argv)
>  {
> @@ -1006,15 +1139,9 @@ static void parse_args(int argc, char **argv)
>              opts.poll_sleep = argtol(optarg, 0);
>              break;
>  
> -        case 'c': /* set new cpu mask for filtering*/
> -            /* Set opts.cpu_mask later as we don't have 'xch' set yet. */
> -            if ( parse_cpumask(optarg) )
> -            {
> -                perror("Not enough memory!");
> -                exit(EXIT_FAILURE);
> -            }
> +        case 'c': /* set new cpu mask for filtering (when xch is set). */
> +            opts.cpu_mask_str = strdup(optarg);
>              break;
> -        
>          case 'e': /* set new event mask for filtering*/
>              parse_evtmask(optarg);
>              break;
> @@ -1079,6 +1206,7 @@ int main(int argc, char **argv)
>      opts.evt_mask = 0;
>      opts.cpu_mask = NULL;
>      opts.cpu_bits = 0;
> +    opts.cpu_mask_str = NULL;
>      opts.disk_rsvd = 0;
>      opts.disable_tracing = 1;
>      opts.start_disabled = 0;
> @@ -1096,10 +1224,8 @@ int main(int argc, char **argv)
>      if ( opts.evt_mask != 0 )
>          set_evt_mask(opts.evt_mask);
>  
> -
> -    set_cpu_mask(opts.cpu_mask, opts.cpu_bits);
> -    /* We don't use it pass this point. */
> -    free(opts.cpu_mask);
> +    if (figure_cpu_mask())
> +        usage(); /* calls exit. */

if (opts.cpu_mask_string)
 figure_cpu_mask();

Thanks,
 -George


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.