]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge commit 'tip/perfcounters/core' into perf-counters-for-linus
authorPeter Zijlstra <a.p.zijlstra@chello.nl>
Wed, 22 Jul 2009 16:05:48 +0000 (18:05 +0200)
committerPeter Zijlstra <a.p.zijlstra@chello.nl>
Wed, 22 Jul 2009 16:05:48 +0000 (18:05 +0200)
1  2 
kernel/perf_counter.c
tools/perf/builtin-report.c

diff --combined kernel/perf_counter.c
index f7a8ab9576e408972f60334427e0e70f15ff622a,8bf997d86bf4f4fc856f167d7e006ad9d7ebd36a..5c6fae4f43d88c08b1532d4b72d009e2327ae7b5
@@@ -146,6 -146,14 +146,14 @@@ static void put_ctx(struct perf_counter
        }
  }
  
+ static void unclone_ctx(struct perf_counter_context *ctx)
+ {
+       if (ctx->parent_ctx) {
+               put_ctx(ctx->parent_ctx);
+               ctx->parent_ctx = NULL;
+       }
+ }
  /*
   * Get the perf_counter_context for a task and lock it.
   * This has to cope with with the fact that until it is locked,
@@@ -1463,10 -1471,8 +1471,8 @@@ static void perf_counter_enable_on_exec
        /*
         * Unclone this context if we enabled any counter.
         */
-       if (enabled && ctx->parent_ctx) {
-               put_ctx(ctx->parent_ctx);
-               ctx->parent_ctx = NULL;
-       }
+       if (enabled)
+               unclone_ctx(ctx);
  
        spin_unlock(&ctx->lock);
  
@@@ -1526,7 -1532,6 +1532,6 @@@ __perf_counter_init_context(struct perf
  
  static struct perf_counter_context *find_get_context(pid_t pid, int cpu)
  {
-       struct perf_counter_context *parent_ctx;
        struct perf_counter_context *ctx;
        struct perf_cpu_context *cpuctx;
        struct task_struct *task;
   retry:
        ctx = perf_lock_task_context(task, &flags);
        if (ctx) {
-               parent_ctx = ctx->parent_ctx;
-               if (parent_ctx) {
-                       put_ctx(parent_ctx);
-                       ctx->parent_ctx = NULL;         /* no longer a clone */
-               }
+               unclone_ctx(ctx);
                spin_unlock_irqrestore(&ctx->lock, flags);
        }
  
@@@ -2968,10 -2969,8 +2969,10 @@@ static void perf_counter_comm_event(str
        struct perf_cpu_context *cpuctx;
        struct perf_counter_context *ctx;
        unsigned int size;
 -      char *comm = comm_event->task->comm;
 +      char comm[TASK_COMM_LEN];
  
 +      memset(comm, 0, sizeof(comm));
 +      strncpy(comm, comm_event->task->comm, sizeof(comm));
        size = ALIGN(strlen(comm)+1, sizeof(u64));
  
        comm_event->comm = comm;
@@@ -3090,15 -3089,8 +3091,15 @@@ static void perf_counter_mmap_event(str
        char *buf = NULL;
        const char *name;
  
 +      memset(tmp, 0, sizeof(tmp));
 +
        if (file) {
 -              buf = kzalloc(PATH_MAX, GFP_KERNEL);
 +              /*
 +               * d_path works from the end of the buffer backwards, so we
 +               * need to add enough zero bytes after the string to handle
 +               * the 64bit alignment we do later.
 +               */
 +              buf = kzalloc(PATH_MAX + sizeof(u64), GFP_KERNEL);
                if (!buf) {
                        name = strncpy(tmp, "//enomem", sizeof(tmp));
                        goto got_name;
                        goto got_name;
                }
        } else {
 -              name = arch_vma_name(mmap_event->vma);
 -              if (name)
 +              if (arch_vma_name(mmap_event->vma)) {
 +                      name = strncpy(tmp, arch_vma_name(mmap_event->vma),
 +                                     sizeof(tmp));
                        goto got_name;
 +              }
  
                if (!vma->vm_mm) {
                        name = strncpy(tmp, "[vdso]", sizeof(tmp));
@@@ -3682,7 -3672,7 +3683,7 @@@ static const struct pmu perf_ops_task_c
  void perf_tpcounter_event(int event_id)
  {
        struct perf_sample_data data = {
 -              .regs = get_irq_regs();
 +              .regs = get_irq_regs(),
                .addr = 0,
        };
  
@@@ -3698,12 -3688,16 +3699,12 @@@ extern void ftrace_profile_disable(int)
  
  static void tp_perf_counter_destroy(struct perf_counter *counter)
  {
 -      ftrace_profile_disable(perf_event_id(&counter->attr));
 +      ftrace_profile_disable(counter->attr.config);
  }
  
  static const struct pmu *tp_perf_counter_init(struct perf_counter *counter)
  {
 -      int event_id = perf_event_id(&counter->attr);
 -      int ret;
 -
 -      ret = ftrace_profile_enable(event_id);
 -      if (ret)
 +      if (ftrace_profile_enable(counter->attr.config))
                return NULL;
  
        counter->destroy = tp_perf_counter_destroy;
@@@ -4262,15 -4256,12 +4263,12 @@@ void perf_counter_exit_task(struct task
         */
        spin_lock(&child_ctx->lock);
        child->perf_counter_ctxp = NULL;
-       if (child_ctx->parent_ctx) {
-               /*
-                * This context is a clone; unclone it so it can't get
-                * swapped to another process while we're removing all
-                * the counters from it.
-                */
-               put_ctx(child_ctx->parent_ctx);
-               child_ctx->parent_ctx = NULL;
-       }
+       /*
+        * If this context is a clone; unclone it so it can't get
+        * swapped to another process while we're removing all
+        * the counters from it.
+        */
+       unclone_ctx(child_ctx);
        spin_unlock(&child_ctx->lock);
        local_irq_restore(flags);
  
index 4b980cce705577ff25970ed21003ce13a82f7bd8,430a195b8589c9de33f4732f81863e7564b638f6..a118bc77286d1d16780c08f348aa9b370225fab8
@@@ -33,8 -33,10 +33,10 @@@ static char         *vmlinux = NULL
  
  static char           default_sort_order[] = "comm,dso";
  static char           *sort_order = default_sort_order;
- static char           *dso_list_str, *comm_list_str, *sym_list_str;
+ static char           *dso_list_str, *comm_list_str, *sym_list_str,
+                       *col_width_list_str;
  static struct strlist *dso_list, *comm_list, *sym_list;
+ static char           *field_sep;
  
  static int            input;
  static int            show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
@@@ -49,6 -51,7 +51,7 @@@ static int            verbose
  static int            modules;
  
  static int            full_paths;
+ static int            show_nr_samples;
  
  static unsigned long  page_size;
  static unsigned long  mmap_window = 32;
@@@ -129,6 -132,33 +132,33 @@@ typedef union event_union 
        struct read_event               read;
  } event_t;
  
+ static int repsep_fprintf(FILE *fp, const char *fmt, ...)
+ {
+       int n;
+       va_list ap;
+       va_start(ap, fmt);
+       if (!field_sep)
+               n = vfprintf(fp, fmt, ap);
+       else {
+               char *bf = NULL;
+               n = vasprintf(&bf, fmt, ap);
+               if (n > 0) {
+                       char *sep = bf;
+                       while (1) {
+                               sep = strchr(sep, *field_sep);
+                               if (sep == NULL)
+                                       break;
+                               *sep = '.';
+                       }
+               }
+               fputs(bf, fp);
+               free(bf);
+       }
+       va_end(ap);
+       return n;
+ }
  static LIST_HEAD(dsos);
  static struct dso *kernel_dso;
  static struct dso *vdso;
@@@ -360,12 -390,28 +390,28 @@@ static struct thread *thread__new(pid_
        return self;
  }
  
+ static unsigned int dsos__col_width,
+                   comms__col_width,
+                   threads__col_width;
  static int thread__set_comm(struct thread *self, const char *comm)
  {
        if (self->comm)
                free(self->comm);
        self->comm = strdup(comm);
-       return self->comm ? 0 : -ENOMEM;
+       if (!self->comm)
+               return -ENOMEM;
+       if (!col_width_list_str && !field_sep &&
+           (!comm_list || strlist__has_entry(comm_list, comm))) {
+               unsigned int slen = strlen(comm);
+               if (slen > comms__col_width) {
+                       comms__col_width = slen;
+                       threads__col_width = slen + 6;
+               }
+       }
+       return 0;
  }
  
  static size_t thread__fprintf(struct thread *self, FILE *fp)
@@@ -536,7 -582,9 +582,9 @@@ struct sort_entry 
  
        int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
        int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
-       size_t  (*print)(FILE *fp, struct hist_entry *);
+       size_t  (*print)(FILE *fp, struct hist_entry *, unsigned int width);
+       unsigned int *width;
+       bool    elide;
  };
  
  static int64_t cmp_null(void *l, void *r)
@@@ -558,15 -606,17 +606,17 @@@ sort__thread_cmp(struct hist_entry *lef
  }
  
  static size_t
- sort__thread_print(FILE *fp, struct hist_entry *self)
+ sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width)
  {
-       return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid);
+       return repsep_fprintf(fp, "%*s:%5d", width - 6,
+                             self->thread->comm ?: "", self->thread->pid);
  }
  
  static struct sort_entry sort_thread = {
-       .header = "         Command:  Pid",
+       .header = "Command:  Pid",
        .cmp    = sort__thread_cmp,
        .print  = sort__thread_print,
+       .width  = &threads__col_width,
  };
  
  /* --sort comm */
@@@ -590,16 -640,17 +640,17 @@@ sort__comm_collapse(struct hist_entry *
  }
  
  static size_t
- sort__comm_print(FILE *fp, struct hist_entry *self)
+ sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width)
  {
-       return fprintf(fp, "%16s", self->thread->comm);
+       return repsep_fprintf(fp, "%*s", width, self->thread->comm);
  }
  
  static struct sort_entry sort_comm = {
-       .header         = "         Command",
+       .header         = "Command",
        .cmp            = sort__comm_cmp,
        .collapse       = sort__comm_collapse,
        .print          = sort__comm_print,
+       .width          = &comms__col_width,
  };
  
  /* --sort dso */
@@@ -617,18 -668,19 +668,19 @@@ sort__dso_cmp(struct hist_entry *left, 
  }
  
  static size_t
- sort__dso_print(FILE *fp, struct hist_entry *self)
+ sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width)
  {
        if (self->dso)
-               return fprintf(fp, "%-25s", self->dso->name);
+               return repsep_fprintf(fp, "%-*s", width, self->dso->name);
  
-       return fprintf(fp, "%016llx         ", (u64)self->ip);
+       return repsep_fprintf(fp, "%*llx", width, (u64)self->ip);
  }
  
  static struct sort_entry sort_dso = {
-       .header = "Shared Object            ",
+       .header = "Shared Object",
        .cmp    = sort__dso_cmp,
        .print  = sort__dso_print,
+       .width  = &dsos__col_width,
  };
  
  /* --sort symbol */
@@@ -648,22 -700,22 +700,22 @@@ sort__sym_cmp(struct hist_entry *left, 
  }
  
  static size_t
- sort__sym_print(FILE *fp, struct hist_entry *self)
+ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used)
  {
        size_t ret = 0;
  
        if (verbose)
-               ret += fprintf(fp, "%#018llx  ", (u64)self->ip);
+               ret += repsep_fprintf(fp, "%#018llx  ", (u64)self->ip);
  
+       ret += repsep_fprintf(fp, "[%c] ", self->level);
        if (self->sym) {
-               ret += fprintf(fp, "[%c] %s",
-                       self->dso == kernel_dso ? 'k' :
-                       self->dso == hypervisor_dso ? 'h' : '.', self->sym->name);
+               ret += repsep_fprintf(fp, "%s", self->sym->name);
  
                if (self->sym->module)
-                       ret += fprintf(fp, "\t[%s]", self->sym->module->name);
+                       ret += repsep_fprintf(fp, "\t[%s]",
+                                            self->sym->module->name);
        } else {
-               ret += fprintf(fp, "%#016llx", (u64)self->ip);
+               ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip);
        }
  
        return ret;
@@@ -690,19 -742,19 +742,19 @@@ sort__parent_cmp(struct hist_entry *lef
  }
  
  static size_t
- sort__parent_print(FILE *fp, struct hist_entry *self)
+ sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width)
  {
-       size_t ret = 0;
-       ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]");
-       return ret;
+       return repsep_fprintf(fp, "%-*s", width,
+                             self->parent ? self->parent->name : "[other]");
  }
  
+ static unsigned int parent_symbol__col_width;
  static struct sort_entry sort_parent = {
-       .header = "Parent symbol       ",
+       .header = "Parent symbol",
        .cmp    = sort__parent_cmp,
        .print  = sort__parent_print,
+       .width  = &parent_symbol__col_width,
  };
  
  static int sort__need_collapse = 0;
@@@ -967,17 -1019,25 +1019,25 @@@ hist_entry__fprintf(FILE *fp, struct hi
                return 0;
  
        if (total_samples)
-               ret = percent_color_fprintf(fp, "   %6.2f%%",
-                               (self->count * 100.0) / total_samples);
+               ret = percent_color_fprintf(fp,
+                                           field_sep ? "%.2f" : "   %6.2f%%",
+                                       (self->count * 100.0) / total_samples);
        else
-               ret = fprintf(fp, "%12Ld ", self->count);
+               ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count);
+       if (show_nr_samples) {
+               if (field_sep)
+                       fprintf(fp, "%c%lld", *field_sep, self->count);
+               else
+                       fprintf(fp, "%11lld", self->count);
+       }
  
        list_for_each_entry(se, &hist_entry__sort_list, list) {
-               if (exclude_other && (se == &sort_parent))
+               if (se->elide)
                        continue;
  
-               fprintf(fp, "  ");
-               ret += se->print(fp, self);
+               fprintf(fp, "%s", field_sep ?: "  ");
+               ret += se->print(fp, self, se->width ? *se->width : 0);
        }
  
        ret += fprintf(fp, "\n");
   *
   */
  
+ static void dso__calc_col_width(struct dso *self)
+ {
+       if (!col_width_list_str && !field_sep &&
+           (!dso_list || strlist__has_entry(dso_list, self->name))) {
+               unsigned int slen = strlen(self->name);
+               if (slen > dsos__col_width)
+                       dsos__col_width = slen;
+       }
+       self->slen_calculated = 1;
+ }
  static struct symbol *
  resolve_symbol(struct thread *thread, struct map **mapp,
               struct dso **dsop, u64 *ipp)
  
        map = thread__find_map(thread, ip);
        if (map != NULL) {
+               /*
+                * We have to do this here as we may have a dso
+                * with no symbol hit that has a name longer than
+                * the ones with symbols sampled.
+                */
+               if (!sort_dso.elide && !map->dso->slen_calculated)
+                       dso__calc_col_width(map->dso);
                if (mapp)
                        *mapp = map;
  got_map:
@@@ -1282,35 -1362,67 +1362,67 @@@ static size_t output__fprintf(FILE *fp
        struct sort_entry *se;
        struct rb_node *nd;
        size_t ret = 0;
+       unsigned int width;
+       char *col_width = col_width_list_str;
  
-       fprintf(fp, "\n");
-       fprintf(fp, "#\n");
-       fprintf(fp, "# (%Ld samples)\n", (u64)total_samples);
+       fprintf(fp, "# Samples: %Ld\n", (u64)total_samples);
        fprintf(fp, "#\n");
  
        fprintf(fp, "# Overhead");
+       if (show_nr_samples) {
+               if (field_sep)
+                       fprintf(fp, "%cSamples", *field_sep);
+               else
+                       fputs("  Samples  ", fp);
+       }
        list_for_each_entry(se, &hist_entry__sort_list, list) {
-               if (exclude_other && (se == &sort_parent))
+               if (se->elide)
+                       continue;
+               if (field_sep) {
+                       fprintf(fp, "%c%s", *field_sep, se->header);
                        continue;
-               fprintf(fp, "  %s", se->header);
+               }
+               width = strlen(se->header);
+               if (se->width) {
+                       if (col_width_list_str) {
+                               if (col_width) {
+                                       *se->width = atoi(col_width);
+                                       col_width = strchr(col_width, ',');
+                                       if (col_width)
+                                               ++col_width;
+                               }
+                       }
+                       width = *se->width = max(*se->width, width);
+               }
+               fprintf(fp, "  %*s", width, se->header);
        }
        fprintf(fp, "\n");
  
+       if (field_sep)
+               goto print_entries;
        fprintf(fp, "# ........");
+       if (show_nr_samples)
+               fprintf(fp, " ..........");
        list_for_each_entry(se, &hist_entry__sort_list, list) {
                unsigned int i;
  
-               if (exclude_other && (se == &sort_parent))
+               if (se->elide)
                        continue;
  
                fprintf(fp, "  ");
-               for (i = 0; i < strlen(se->header); i++)
+               if (se->width)
+                       width = *se->width;
+               else
+                       width = strlen(se->header);
+               for (i = 0; i < width; i++)
                        fprintf(fp, ".");
        }
        fprintf(fp, "\n");
  
        fprintf(fp, "#\n");
  
+ print_entries:
        for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
                pos = rb_entry(nd, struct hist_entry, rb_node);
                ret += hist_entry__fprintf(fp, pos, total_samples);
@@@ -1883,6 -1995,8 +1995,8 @@@ static const struct option options[] = 
        OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
        OPT_BOOLEAN('m', "modules", &modules,
                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
+       OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples,
+                   "Show a column with the number of samples"),
        OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
                   "sort by key(s): pid, comm, dso, symbol, parent"),
        OPT_BOOLEAN('P', "full-paths", &full_paths,
                   "regex filter to identify parent, see: '--sort parent'"),
        OPT_BOOLEAN('x', "exclude-other", &exclude_other,
                    "Only display entries with parent-match"),
 -      OPT_CALLBACK_DEFAULT('c', "callchain", NULL, "output_type,min_percent",
 +      OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
                     "Display callchains using output_type and min percent threshold. "
 -                   "Default: flat,0", &parse_callchain_opt, callchain_default_opt),
 +                   "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
        OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",
                   "only consider symbols in these dsos"),
        OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",
                   "only consider symbols in these comms"),
        OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
                   "only consider these symbols"),
+       OPT_STRING('w', "column-widths", &col_width_list_str,
+                  "width[,width...]",
+                  "don't try to adjust column width, use these fixed values"),
+       OPT_STRING('t', "field-separator", &field_sep, "separator",
+                  "separator for columns, no spaces will be added between "
+                  "columns '.' is reserved."),
        OPT_END()
  };
  
@@@ -1919,7 -2039,8 +2039,8 @@@ static void setup_sorting(void
  }
  
  static void setup_list(struct strlist **list, const char *list_str,
-                      const char *list_name)
+                      struct sort_entry *se, const char *list_name,
+                      FILE *fp)
  {
        if (list_str) {
                *list = strlist__new(true, list_str);
                                list_name);
                        exit(129);
                }
+               if (strlist__nr_entries(*list) == 1) {
+                       fprintf(fp, "# %s: %s\n", list_name,
+                               strlist__entry(*list, 0)->s);
+                       se->elide = true;
+               }
        }
  }
  
@@@ -1941,9 -2067,10 +2067,10 @@@ int cmd_report(int argc, const char **a
  
        setup_sorting();
  
-       if (parent_pattern != default_parent_pattern)
+       if (parent_pattern != default_parent_pattern) {
                sort_dimension__add("parent");
-       else
+               sort_parent.elide = 1;
+       } else
                exclude_other = 0;
  
        /*
        if (argc)
                usage_with_options(report_usage, options);
  
-       setup_list(&dso_list, dso_list_str, "dso");
-       setup_list(&comm_list, comm_list_str, "comm");
-       setup_list(&sym_list, sym_list_str, "symbol");
        setup_pager();
  
+       setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout);
+       setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout);
+       setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout);
+       if (field_sep && *field_sep == '.') {
+               fputs("'.' is the only non valid --field-separator argument\n",
+                     stderr);
+               exit(129);
+       }
        return __cmd_report();
  }