David Vrabel
2012-Oct-04 12:28 UTC
[PATCH 0 of 2] xenalyze: decode new hypercall trace records (v2)
This series allows xenalyze to decode the new PV_HYPERCALL_V2 and PV_HYPERCALL_SUBCALL trace records being proposed for Xen 4.3. Changes since v1: - pv_hypercall_gather_args() instead of pv_hypercall_arg(). - Refactor *_cmd_to_str. David
David Vrabel
2012-Oct-04 12:28 UTC
[PATCH 1 of 2] xenalyze: decode PV_HYPERCALL_V2 records
Newer version of Xen produce TRC_PV_HYPERCALL_V2 records instead of the older TRC_PV_HYPERCALL format. This updated format doesn''t included the IP but it does include select hypercall arguments. Signed-off-by: David Vrabel <david.vrabel@citrix.com> diff --git a/analyze.h b/analyze.h --- a/analyze.h +++ b/analyze.h @@ -3,6 +3,8 @@ #include <stdint.h> +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + #define TRC_GEN_MAIN 0 #define TRC_SCHED_MAIN 1 #define TRC_DOM0OP_MAIN 2 diff --git a/pv.h b/pv.h new file mode 100644 --- /dev/null +++ b/pv.h @@ -0,0 +1,41 @@ +/* + * PV event decoding. + * + * Copyright (C) 2012 Citrix Systems R&D Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ +#ifndef __PV_H + +#include "analyze.h" +#include "trace.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARG_MISSING 0x0 +#define ARG_32BIT 0x1 +#define ARG_64BIT 0x2 + +#define MMU_UPDATE_PREEMPTED (~(~0U>>1)) + +static inline uint32_t pv_hypercall_op(const struct record_info *ri) +{ + return ri->d[0] & ~TRC_PV_HYPERCALL_V2_ARG_MASK; +} + +static inline int pv_hypercall_arg_present(const struct record_info *ri, int arg) +{ + return (ri->d[0] >> (20 + 2*arg)) & 0x3; +} + +void pv_hypercall_gather_args(const struct record_info *ri, uint64_t *args); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/xenalyze.c b/xenalyze.c --- a/xenalyze.c +++ b/xenalyze.c @@ -32,6 +32,7 @@ #include "trace.h" #include "analyze.h" #include "mread.h" +#include "pv.h" #include <errno.h> #include <strings.h> #include <string.h> @@ -1485,6 +1486,7 @@ enum { PV_GDT_LDT_MAPPING_FAULT, PV_PTWR_EMULATION, PV_PTWR_EMULATION_PAE, + PV_HYPERCALL_V2 = 13, PV_MAX }; @@ -1499,7 +1501,9 @@ char *pv_name[PV_MAX] = { [PV_PAGING_FIXUP]="paging fixup", [PV_GDT_LDT_MAPPING_FAULT]="gdt/ldt mapping fault", [PV_PTWR_EMULATION]="ptwr", - [PV_PTWR_EMULATION_PAE]="ptwr(pae)" + [PV_PTWR_EMULATION_PAE]="ptwr(pae)", + [PV_HYPERCALL_V2]="hypercall", + [PV_HYPERCALL_SUBCALL]="hypercall (subcall)", }; #define PV_HYPERCALL_MAX 56 @@ -6500,10 +6504,20 @@ void pv_summary(struct pv_data *pv) { printf("PV events:\n"); for(i=0; i<PV_MAX; i++) { - if(pv->count[i]) - printf(" %s %d\n", pv_name[i], pv->count[i]); + int count; + + count = pv->count[i]; + if (i == PV_HYPERCALL_V2) + count += pv->count[PV_HYPERCALL_SUBCALL]; + + if (count == 0) + continue; + + printf(" %s %d\n", pv_name[i], count); + switch(i) { case PV_HYPERCALL: + case PV_HYPERCALL_V2: for(j=0; j<PV_HYPERCALL_MAX; j++) { if(pv->hypercall_count[j]) printf(" %-29s[%2d]: %6d\n", @@ -6523,6 +6537,145 @@ void pv_summary(struct pv_data *pv) { } } +static const char *grant_table_op_str[] = { + "map_grant_ref", "unmap_grant_ref", "setup_table", "dump_table", + "transfer", "copy", "query_size", "unmap_and_replace", + "set_version", "get_status_frames", "get_version", "swap_grant_ref", +}; + +static const char *vcpu_op_str[] = { + "initialise", "up", "down", "is_up", "get_runstate_info", + "register_runstate_memory_area", "set_periodic_timer", + "stop_periodic_timer", "set_singleshot_timer", "stop_singleshot_timer", + "register_vcpu_info", "send_nmi", "get_physid", + "register_vcpu_time_memory_area", +}; + +static const char *sched_op_str[] = { + "yield", "block", "shutdown", "poll", "remote_shutdown", "shutdown_code", + "watchdog", +}; + +static const char *cmd_to_str(const char *strings[], size_t n, uint32_t cmd) +{ + static char buf[32]; + + if (cmd < n) + return strings[cmd]; + + snprintf(buf, sizeof(buf), "unknown (%d)", cmd); + return buf; +} + +#define CMD_TO_STR(op) \ + static const char * op ## _to_str(uint32_t cmd) { \ + return cmd_to_str(op ## _str, ARRAY_SIZE(op ## _str), cmd); \ + } + +CMD_TO_STR(grant_table_op); +CMD_TO_STR(vcpu_op); +CMD_TO_STR(sched_op); + +void pv_hypercall_gather_args(const struct record_info *ri, uint64_t *args) +{ + int i, word; + + /* Missing arguments are zeroed. */ + memset(args, 0, 6 * sizeof(uint64_t)); + + for (i = 0, word = 1; i < 6 && word < ri->extra_words; i++) { + int present = pv_hypercall_arg_present(ri, i); + + switch (present) { + case ARG_32BIT: + args[i] = ri->d[word]; + break; + case ARG_64BIT: + args[i] = ((uint64_t)ri->d[word + 1] << 32) | ri->d[word]; + break; + } + + /* Skip over any words for this argument. */ + word += present; + } +} + +static void pv_hypercall_print_args(const struct record_info *ri) +{ + int i, word; + + for (i = 0, word = 1; i < 6 && word < ri->extra_words; i++) { + int present = pv_hypercall_arg_present(ri, i); + + switch (present) { + case ARG_MISSING: + printf(" ??"); + break; + case ARG_32BIT: + printf(" %08x", ri->d[word]); + break; + case ARG_64BIT: + printf(" %016"PRIu64"", ((uint64_t)ri->d[word + 1] << 32) | ri->d[word]); + break; + } + + word += present; + } +} + +void pv_hypercall_v2_process(struct record_info *ri, struct pv_data *pv) +{ + int op = pv_hypercall_op(ri); + + if(opt.summary_info) { + if(op < PV_HYPERCALL_MAX) + pv->hypercall_count[op]++; + } + + if(opt.dump_all) { + uint64_t args[6]; + + if(op < HYPERCALL_MAX) + printf(" %s hypercall %2x (%s)", + ri->dump_header, op, hypercall_name[op]); + else + printf(" %s hypercall %2x", + ri->dump_header, op); + + switch(op) { + case HYPERCALL_mmu_update: + pv_hypercall_gather_args(ri, args); + printf(" %d updates%s", (uint32_t)args[1] & ~MMU_UPDATE_PREEMPTED, + (args[1] & MMU_UPDATE_PREEMPTED) ? " (preempted)" : ""); + break; + case HYPERCALL_multicall: + pv_hypercall_gather_args(ri, args); + printf(" %d calls", (uint32_t)args[1]); + break; + case HYPERCALL_grant_table_op: + pv_hypercall_gather_args(ri, args); + printf(" %s %d ops", grant_table_op_to_str(args[0]), (uint32_t)args[2]); + break; + case HYPERCALL_vcpu_op: + pv_hypercall_gather_args(ri, args); + printf(" %s vcpu %d", vcpu_op_to_str(args[0]), (uint32_t)args[1]); + break; + case HYPERCALL_mmuext_op: + pv_hypercall_gather_args(ri, args); + printf(" %d ops", (uint32_t)args[1]); + break; + case HYPERCALL_sched_op: + pv_hypercall_gather_args(ri, args); + printf(" %s", sched_op_to_str(args[0])); + break; + default: + pv_hypercall_print_args(ri); + break; + } + printf("\n"); + } +} + void pv_process(struct pcpu_info *p) { struct record_info *ri = &p->ri; @@ -6555,9 +6708,9 @@ void pv_process(struct pcpu_info *p) case PV_PTWR_EMULATION_PAE: pv_ptwr_emulation_process(ri, pv); break; - case PV_PAGE_FAULT: - //pv_pf_process(ri, pv); - //break; + case PV_HYPERCALL_V2: + pv_hypercall_v2_process(ri, pv); + break; default: pv_generic_process(ri, pv); break;
David Vrabel
2012-Oct-04 12:28 UTC
[PATCH 2 of 2] xenalyze: decode PV_HYPERCALL_SUBCALL events
Decode the PV_HYPERCALL_SUBCALL events which are used for calls within a multicall hypercall. They are indented so its easier to see which multicall they were part of. Signed-off-by: David Vrabel <david.vrabel@citrix.com> Acked-by: George Dunlap <george.dunlap@citrix.com> diff --git a/xenalyze.c b/xenalyze.c --- a/xenalyze.c +++ b/xenalyze.c @@ -1487,6 +1487,7 @@ enum { PV_PTWR_EMULATION, PV_PTWR_EMULATION_PAE, PV_HYPERCALL_V2 = 13, + PV_HYPERCALL_SUBCALL = 14, PV_MAX }; @@ -6623,7 +6624,8 @@ static void pv_hypercall_print_args(cons } } -void pv_hypercall_v2_process(struct record_info *ri, struct pv_data *pv) +void pv_hypercall_v2_process(struct record_info *ri, struct pv_data *pv, + const char *indent) { int op = pv_hypercall_op(ri); @@ -6636,11 +6638,11 @@ void pv_hypercall_v2_process(struct reco uint64_t args[6]; if(op < HYPERCALL_MAX) - printf(" %s hypercall %2x (%s)", - ri->dump_header, op, hypercall_name[op]); + printf(" %s%s hypercall %2x (%s)", + ri->dump_header, indent, op, hypercall_name[op]); else - printf(" %s hypercall %2x", - ri->dump_header, op); + printf(" %s%s hypercall %2x", + ri->dump_header, indent, op); switch(op) { case HYPERCALL_mmu_update: @@ -6709,7 +6711,10 @@ void pv_process(struct pcpu_info *p) pv_ptwr_emulation_process(ri, pv); break; case PV_HYPERCALL_V2: - pv_hypercall_v2_process(ri, pv); + pv_hypercall_v2_process(ri, pv, ""); + break; + case PV_HYPERCALL_SUBCALL: + pv_hypercall_v2_process(ri, pv, " "); break; default: pv_generic_process(ri, pv);
George Dunlap
2012-Oct-10 13:26 UTC
Re: [PATCH 1 of 2] xenalyze: decode PV_HYPERCALL_V2 records
On Thu, Oct 4, 2012 at 1:28 PM, David Vrabel <david.vrabel@citrix.com> wrote:> Newer version of Xen produce TRC_PV_HYPERCALL_V2 records instead of > the older TRC_PV_HYPERCALL format. This updated format doesn''t > included the IP but it does include select hypercall arguments. > > Signed-off-by: David Vrabel <david.vrabel@citrix.com>You had a couple of stray SUBCALL things in this patch which cause it to fail compilation; I moved them to the next patch and have committed both patches. Thanks. -George.> > diff --git a/analyze.h b/analyze.h > --- a/analyze.h > +++ b/analyze.h > @@ -3,6 +3,8 @@ > > #include <stdint.h> > > +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) > + > #define TRC_GEN_MAIN 0 > #define TRC_SCHED_MAIN 1 > #define TRC_DOM0OP_MAIN 2 > diff --git a/pv.h b/pv.h > new file mode 100644 > --- /dev/null > +++ b/pv.h > @@ -0,0 +1,41 @@ > +/* > + * PV event decoding. > + * > + * Copyright (C) 2012 Citrix Systems R&D Ltd. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + */ > +#ifndef __PV_H > + > +#include "analyze.h" > +#include "trace.h" > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#define ARG_MISSING 0x0 > +#define ARG_32BIT 0x1 > +#define ARG_64BIT 0x2 > + > +#define MMU_UPDATE_PREEMPTED (~(~0U>>1)) > + > +static inline uint32_t pv_hypercall_op(const struct record_info *ri) > +{ > + return ri->d[0] & ~TRC_PV_HYPERCALL_V2_ARG_MASK; > +} > + > +static inline int pv_hypercall_arg_present(const struct record_info *ri, int arg) > +{ > + return (ri->d[0] >> (20 + 2*arg)) & 0x3; > +} > + > +void pv_hypercall_gather_args(const struct record_info *ri, uint64_t *args); > + > +#ifdef __cplusplus > +} /* extern "C" */ > +#endif > + > +#endif > diff --git a/xenalyze.c b/xenalyze.c > --- a/xenalyze.c > +++ b/xenalyze.c > @@ -32,6 +32,7 @@ > #include "trace.h" > #include "analyze.h" > #include "mread.h" > +#include "pv.h" > #include <errno.h> > #include <strings.h> > #include <string.h> > @@ -1485,6 +1486,7 @@ enum { > PV_GDT_LDT_MAPPING_FAULT, > PV_PTWR_EMULATION, > PV_PTWR_EMULATION_PAE, > + PV_HYPERCALL_V2 = 13, > PV_MAX > }; > > @@ -1499,7 +1501,9 @@ char *pv_name[PV_MAX] = { > [PV_PAGING_FIXUP]="paging fixup", > [PV_GDT_LDT_MAPPING_FAULT]="gdt/ldt mapping fault", > [PV_PTWR_EMULATION]="ptwr", > - [PV_PTWR_EMULATION_PAE]="ptwr(pae)" > + [PV_PTWR_EMULATION_PAE]="ptwr(pae)", > + [PV_HYPERCALL_V2]="hypercall", > + [PV_HYPERCALL_SUBCALL]="hypercall (subcall)", > }; > > #define PV_HYPERCALL_MAX 56 > @@ -6500,10 +6504,20 @@ void pv_summary(struct pv_data *pv) { > > printf("PV events:\n"); > for(i=0; i<PV_MAX; i++) { > - if(pv->count[i]) > - printf(" %s %d\n", pv_name[i], pv->count[i]); > + int count; > + > + count = pv->count[i]; > + if (i == PV_HYPERCALL_V2) > + count += pv->count[PV_HYPERCALL_SUBCALL]; > + > + if (count == 0) > + continue; > + > + printf(" %s %d\n", pv_name[i], count); > + > switch(i) { > case PV_HYPERCALL: > + case PV_HYPERCALL_V2: > for(j=0; j<PV_HYPERCALL_MAX; j++) { > if(pv->hypercall_count[j]) > printf(" %-29s[%2d]: %6d\n", > @@ -6523,6 +6537,145 @@ void pv_summary(struct pv_data *pv) { > } > } > > +static const char *grant_table_op_str[] = { > + "map_grant_ref", "unmap_grant_ref", "setup_table", "dump_table", > + "transfer", "copy", "query_size", "unmap_and_replace", > + "set_version", "get_status_frames", "get_version", "swap_grant_ref", > +}; > + > +static const char *vcpu_op_str[] = { > + "initialise", "up", "down", "is_up", "get_runstate_info", > + "register_runstate_memory_area", "set_periodic_timer", > + "stop_periodic_timer", "set_singleshot_timer", "stop_singleshot_timer", > + "register_vcpu_info", "send_nmi", "get_physid", > + "register_vcpu_time_memory_area", > +}; > + > +static const char *sched_op_str[] = { > + "yield", "block", "shutdown", "poll", "remote_shutdown", "shutdown_code", > + "watchdog", > +}; > + > +static const char *cmd_to_str(const char *strings[], size_t n, uint32_t cmd) > +{ > + static char buf[32]; > + > + if (cmd < n) > + return strings[cmd]; > + > + snprintf(buf, sizeof(buf), "unknown (%d)", cmd); > + return buf; > +} > + > +#define CMD_TO_STR(op) \ > + static const char * op ## _to_str(uint32_t cmd) { \ > + return cmd_to_str(op ## _str, ARRAY_SIZE(op ## _str), cmd); \ > + } > + > +CMD_TO_STR(grant_table_op); > +CMD_TO_STR(vcpu_op); > +CMD_TO_STR(sched_op); > + > +void pv_hypercall_gather_args(const struct record_info *ri, uint64_t *args) > +{ > + int i, word; > + > + /* Missing arguments are zeroed. */ > + memset(args, 0, 6 * sizeof(uint64_t)); > + > + for (i = 0, word = 1; i < 6 && word < ri->extra_words; i++) { > + int present = pv_hypercall_arg_present(ri, i); > + > + switch (present) { > + case ARG_32BIT: > + args[i] = ri->d[word]; > + break; > + case ARG_64BIT: > + args[i] = ((uint64_t)ri->d[word + 1] << 32) | ri->d[word]; > + break; > + } > + > + /* Skip over any words for this argument. */ > + word += present; > + } > +} > + > +static void pv_hypercall_print_args(const struct record_info *ri) > +{ > + int i, word; > + > + for (i = 0, word = 1; i < 6 && word < ri->extra_words; i++) { > + int present = pv_hypercall_arg_present(ri, i); > + > + switch (present) { > + case ARG_MISSING: > + printf(" ??"); > + break; > + case ARG_32BIT: > + printf(" %08x", ri->d[word]); > + break; > + case ARG_64BIT: > + printf(" %016"PRIu64"", ((uint64_t)ri->d[word + 1] << 32) | ri->d[word]); > + break; > + } > + > + word += present; > + } > +} > + > +void pv_hypercall_v2_process(struct record_info *ri, struct pv_data *pv) > +{ > + int op = pv_hypercall_op(ri); > + > + if(opt.summary_info) { > + if(op < PV_HYPERCALL_MAX) > + pv->hypercall_count[op]++; > + } > + > + if(opt.dump_all) { > + uint64_t args[6]; > + > + if(op < HYPERCALL_MAX) > + printf(" %s hypercall %2x (%s)", > + ri->dump_header, op, hypercall_name[op]); > + else > + printf(" %s hypercall %2x", > + ri->dump_header, op); > + > + switch(op) { > + case HYPERCALL_mmu_update: > + pv_hypercall_gather_args(ri, args); > + printf(" %d updates%s", (uint32_t)args[1] & ~MMU_UPDATE_PREEMPTED, > + (args[1] & MMU_UPDATE_PREEMPTED) ? " (preempted)" : ""); > + break; > + case HYPERCALL_multicall: > + pv_hypercall_gather_args(ri, args); > + printf(" %d calls", (uint32_t)args[1]); > + break; > + case HYPERCALL_grant_table_op: > + pv_hypercall_gather_args(ri, args); > + printf(" %s %d ops", grant_table_op_to_str(args[0]), (uint32_t)args[2]); > + break; > + case HYPERCALL_vcpu_op: > + pv_hypercall_gather_args(ri, args); > + printf(" %s vcpu %d", vcpu_op_to_str(args[0]), (uint32_t)args[1]); > + break; > + case HYPERCALL_mmuext_op: > + pv_hypercall_gather_args(ri, args); > + printf(" %d ops", (uint32_t)args[1]); > + break; > + case HYPERCALL_sched_op: > + pv_hypercall_gather_args(ri, args); > + printf(" %s", sched_op_to_str(args[0])); > + break; > + default: > + pv_hypercall_print_args(ri); > + break; > + } > + printf("\n"); > + } > +} > + > void pv_process(struct pcpu_info *p) > { > struct record_info *ri = &p->ri; > @@ -6555,9 +6708,9 @@ void pv_process(struct pcpu_info *p) > case PV_PTWR_EMULATION_PAE: > pv_ptwr_emulation_process(ri, pv); > break; > - case PV_PAGE_FAULT: > - //pv_pf_process(ri, pv); > - //break; > + case PV_HYPERCALL_V2: > + pv_hypercall_v2_process(ri, pv); > + break; > default: > pv_generic_process(ri, pv); > break; > > _______________________________________________ > Xen-devel mailing list > Xen-devel@lists.xen.org > http://lists.xen.org/xen-devel