Enable multiple hist_entry_group groups in the output based on a sort
method.

Currently only 'perf report' is hooked in with '--group-sort='.  The choices
are cpu, pid, and cacheline.  Only --stdio works right now.  I haven't figured
out how the other outputs work.

Sample output from 'perf mem record -a grep -r foo /* > /dev/null'

(normal) perf mem report --percent-limit=1.0 --stdio

 Overhead       Samples
  Local Weight             Memory access                                      
Symbol
 ........  ............  ............  ........................  
........................

     4.13%             1  1759          Uncached hit              [k] 
ahci_scr_read
     1.16%             1  492           L1 hit                    [k] 
_raw_read_lock

(cpu groups) perf mem report --group-sort=cpu --percent-limit=1.0 --stdio

 Overhead       Samples  CPU
  Local Weight             Memory access                                      
Symbol
 ........  ............  ............  ........................  
........................

    28.80%          1239   25
             3.07%               377           L1 hit                    [k] 
complete_walk
             2.76%               339           LFB hit                   [k] 
update_cfs_shares
             2.66%               326           LFB hit                   [k] 
copy_user_enhanced_f
             2.11%               259           Local RAM hit             [k] 
copy_user_enhanced_f
             1.84%               226           LFB hit                   [k] 
copy_user_enhanced_f
             1.74%               213           LFB hit                   [k] 
copy_user_enhanced_f
             1.53%               187           LFB hit                   [k] 
copy_user_enhanced_f
             1.04%               128           LFB hit                   [k] 
copy_user_enhanced_f
             1.01%               124           LFB hit                   [k] 
copy_user_enhanced_f
    27.44%           990    7
            15.06%               1759          Uncached hit              [k] 
ahci_scr_read
             4.21%               492           L1 hit                    [k] 
_raw_read_lock
             1.04%               122           LFB hit                   [k] 
find_busiest_group
             1.02%            1  7             L1 hit                    [.] 
__gconv_transform_ut
    20.34%          1010    0
             4.04%            5  7             L1 hit                    [k] 
poll_idle
             3.56%               308           Local RAM hit             [k] 
copy_user_enhanced_f
             2.59%               224           L3 hit                    [k] 
copy_user_enhanced_f
             2.12%               184           Local RAM hit             [k] 
copy_user_enhanced_f
             1.54%            1  7             L1 hit                    [.] 
__gconv_transform_ut

(pid groups) perf mem report --group-sort=pid --percent-limit=1.0 --stdio

 Overhead       Samples       Command:  Pid
  Local Weight             Memory access                                      
Symbol
 ........  ............  ............  ........................  
........................

    67.98%          3023          grep:77842
             1.70%               492           L1 hit                    [k] 
_raw_read_lock
             1.30%               377           L1 hit                    [k] 
complete_walk
             1.17%               339           LFB hit                   [k] 
update_cfs_shares
             1.13%               326           LFB hit                   [k] 
copy_user_enhanced_f
             1.09%            4  7             L1 hit                    [.] 
__gconv_transform_ut
             1.06%               308           Local RAM hit             [k] 
copy_user_enhanced_f
    23.39%           660       swapper:    0
            17.66%               1759          Uncached hit              [k] 
ahci_scr_read
             4.17%               415           L1 hit                    [k] 
handle_irq
             3.51%            5  7             L1 hit                    [k] 
poll_idle
             3.34%               333           Remote Cache (1 hop) hit  [k] 
update_cfs_rq_blocke
             2.79%               278           LFB hit                   [k] 
add_interrupt_random
             2.69%               268           L1 hit                    [k] 
update_cpu_load_nohz
             2.69%               268           Local RAM hit             [k] 
find_busiest_group
             2.51%               250           LFB hit                   [k] 
ktime_get
             2.45%               244           L1 hit                    [k] 
rcu_eqs_exit_common.
             1.89%               188           LFB hit                   [k] 
ktime_get
             1.25%               124           LFB hit                   [k] 
get_next_timer_inter
     5.90%           186     rcu_sched:    8
             6.14%               154           L1 hit                    [k] 
_raw_spin_lock_irq
             4.42%               111           L1 hit                    [k] 
_raw_spin_lock_irqsa
             3.90%               49            L3 hit                    [k] 
find_busiest_group
             3.11%               78            LFB hit                   [k] 
find_busiest_group
             3.11%               78            LFB hit                   [k] 
find_busiest_group
             2.39%               60            LFB hit                   [k] 
idle_cpu
             2.27%               57            L3 hit                    [k] 
idle_cpu
             2.27%               57            L3 hit                    [k] 
idle_cpu
             2.19%               55            L3 hit                    [k] 
target_load

(cacheline groups) perf mem report --group-sort=cacheline --percent-limit=1.0 
--stdio

 Overhead       Samples  Cacheline
  Local Weight             Memory access                                      
Symbol
 ........  ............  ............  ........................  
........................

     4.67%           284  [.] 0x0000000000b030c0
             1.76%               7             L1 hit                    [.] 
0x0000000000008651
             1.41%               7             L1 hit                    [.] 
0x00000000000085cc
             1.41%               7             L1 hit                    [.] 
0x00000000000085e4
             1.41%               7             L1 hit                    [.] 
0x00000000000085e4
             1.41%               7             L1 hit                    [.] 
0x00000000000085f8
             1.41%               7             L1 hit                    [.] 
0x0000000000008624
             1.06%               7             L1 hit                    [.] 
0x00000000000085d8
             1.06%               7             L1 hit                    [.] 
0x00000000000085e4
             1.06%               7             L1 hit                    [.] 
0x00000000000085e4
             1.06%               7             L1 hit                    [.] 
0x0000000000008610
             1.06%               7             L1 hit                    [.] 
0x0000000000008610
             1.06%               7             L1 hit                    [.] 
0x0000000000008624
             1.06%               7             L1 hit                    [.] 
0x0000000000008624
             1.06%               7             L1 hit                    [.] 
0x0000000000008645
             1.06%               7             L1 hit                    [.] 
0x0000000000008645
             1.06%               7             L1 hit                    [.] 
0x0000000000008645
             1.06%               7             L1 hit                    [.] 
0x0000000000008651
             1.06%               7             L1 hit                    [.] 
0x0000000000008651
     4.26%           250  [.] 0x0000000000b03080
             3.91%               71            L3 hit                    [.] 
0x0000000000008651
             1.54%               7             L1 hit                    [.] 
0x00000000000085d8
             1.16%               7             L1 hit                    [.] 
0x00000000000085cc
             1.16%               7             L1 hit                    [.] 
0x00000000000085f8
             1.16%               7             L1 hit                    [.] 
0x0000000000008604
             1.16%               7             L1 hit                    [.] 
0x0000000000008624
             1.16%               7             L1 hit                    [.] 
0x0000000000008645
             1.16%               7             L1 hit                    [.] 
0x0000000000008645
             1.16%               7             L1 hit                    [.] 
0x0000000000008645
     4.13%             1  [k] 0xffffc90000062180
           100.00%               1759          Uncached hit              [k] 
ahci_scr_read
     3.44%           209  [.] 0x0000000000b03040
             6.70%            1  7             L1 hit                    [.] 
0x00000000000085e4
             6.22%            1  7             L1 hit                    [.] 
0x0000000000008624
             5.74%            1  7             L1 hit                    [.] 
0x00000000000085cc
             5.74%            1  7             L1 hit                    [.] 
0x0000000000008610
             5.74%            1  7             L1 hit                    [.] 
0x0000000000008645
             5.26%            1  7             L1 hit                    [.] 
0x00000000000085d8
             5.26%            1  7             L1 hit                    [.] 
0x0000000000008651
             4.78%            1  7             L1 hit                    [.] 
0x00000000000085f8
             4.78%            1  7             L1 hit                    [.] 
0x0000000000008604
             2.87%               7             L1 hit                    [.] 
0x0000000000008630
             1.44%               7             L1 hit                    [.] 
0x00000000000085f8

Signed-off-by: Don Zickus <[email protected]>
---
 tools/perf/builtin-report.c |   2 +
 tools/perf/ui/gtk/hists.c   |  10 +++
 tools/perf/ui/hist.c        |  19 +++++
 tools/perf/ui/stdio/hist.c  |  69 ++++++++++++++++-
 tools/perf/util/hist.c      |  17 +++++
 tools/perf/util/hist.h      |   3 +
 tools/perf/util/sort.c      | 181 +++++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/sort.h      |   4 +
 8 files changed, 301 insertions(+), 4 deletions(-)

diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 51a37d6..010271e 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -764,6 +764,8 @@ int cmd_report(int argc, const char **argv, const char 
*prefix __maybe_unused)
                   " dso_to, dso_from, symbol_to, symbol_from, mispredict,"
                   " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, "
                   "snoop, locked, abort, in_tx, transaction"),
+       OPT_STRING(0, "group-sort", &group_sort_order, "key[,key2...]",
+                  "group sort by key(s): pid, cacheline"),
        OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
                    "Show sample percentage for different cpu modes"),
        OPT_STRING('p', "parent", &parent_pattern, "regex",
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index befbf3b..ab9c96a 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -200,6 +200,16 @@ static void perf_gtk__show_hists(GtkWidget *window, struct 
hists *hists,
                                                            col_idx++, NULL);
        }
 
+       list_for_each_entry(se, &hist_group__sort_list, list) {
+               if (se->elide)
+                       continue;
+
+               gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
+                                                           -1, se->se_header,
+                                                           renderer, "text",
+                                                           col_idx++, NULL);
+       }
+
        list_for_each_entry(se, &hist_entry__sort_list, list) {
                if (se->elide)
                        continue;
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index 21c4e5c..31c74b3 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -319,6 +319,25 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char 
*s, size_t size,
        return ret;
 }
 
+int hist_entry_group__sort_snprintf(struct hist_entry *he, char *s, size_t 
size,
+                             struct hists *hists)
+{
+       const char *sep = symbol_conf.field_sep;
+       struct sort_entry *se;
+       int ret = 0;
+
+       list_for_each_entry(se, &hist_group__sort_list, list) {
+               if (se->elide)
+                       continue;
+
+               ret += scnprintf(s + ret, size - ret, "%s", sep ?: "  ");
+               ret += se->se_snprintf(he, s + ret, size - ret,
+                                      hists__col_len(hists, se->se_width_idx));
+       }
+
+       return ret;
+}
+
 /*
  * See hists__fprintf to match the column widths
  */
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 6c1a85a..c3d3c1b 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -354,6 +354,10 @@ static int hist_entry__fprintf(struct hist_entry *he, 
size_t size,
        if (size == 0 || size > bfsz)
                size = hpp.size = bfsz;
 
+       if (sort__has_group) {
+               *hpp.buf++ = '\t';
+               hpp.size--;
+       }
        ret = hist_entry__period_snprintf(&hpp, he);
        hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
 
@@ -365,6 +369,28 @@ static int hist_entry__fprintf(struct hist_entry *he, 
size_t size,
        return ret;
 }
 
+static int hist_entry_group__fprintf(struct hist_entry *he, size_t size,
+                                    struct hists *hists,
+                                    char *bf, size_t bfsz, FILE *fp)
+{
+       int ret;
+       struct perf_hpp hpp = {
+               .buf            = bf,
+               .size           = size,
+               .total_period   = he->groups->hists->stats.total_period,
+       };
+
+       if (size == 0 || size > bfsz)
+               size = hpp.size = bfsz;
+
+       ret = hist_entry__period_snprintf(&hpp, he);
+       hist_entry_group__sort_snprintf(he, bf + ret, size - ret, hists);
+
+       ret = fprintf(fp, "%s\n", bf);
+
+       return ret;
+}
+
 size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
                      int max_cols, float min_pcnt, FILE *fp)
 {
@@ -403,6 +429,32 @@ size_t hists__fprintf(struct hists *hists, bool 
show_header, int max_rows,
                fprintf(fp, "%s", bf);
        }
 
+       list_for_each_entry(se, &hist_group__sort_list, list) {
+               if (se->elide)
+                       continue;
+               if (sep) {
+                       fprintf(fp, "%c%s", *sep, se->se_header);
+                       continue;
+               }
+               width = strlen(se->se_header);
+               if (symbol_conf.col_width_list_str) {
+                       if (col_width) {
+                               hists__set_col_len(hists, se->se_width_idx,
+                                                  atoi(col_width));
+                               col_width = strchr(col_width, ',');
+                               if (col_width)
+                                       ++col_width;
+                       }
+               }
+               if (!hists__new_col_len(hists, se->se_width_idx, width))
+                       width = hists__col_len(hists, se->se_width_idx);
+               fprintf(fp, "  %*s", width, se->se_header);
+       }
+
+       fprintf(fp, "\n");
+       if (max_rows && ++nr_rows >= max_rows)
+               goto out;
+
        list_for_each_entry(se, &hist_entry__sort_list, list) {
                if (se->elide)
                        continue;
@@ -481,9 +533,22 @@ print_entries:
        }
 
        hist__for_each_group_out(g, hists) {
-               hist_group__for_each_entry_out(h, g) {
+               float percent = g->entry.stat.period * 100.0 /
+                                       hists->stats.total_period;
+
+               if (percent < min_pcnt)
+                       continue;
 
-                       float percent = h->stat.period * 100.0 /
+               if (sort__has_group) {
+                       ret += hist_entry_group__fprintf(&g->entry, max_cols, 
hists,
+                                                        line, linesz, fp);
+
+                       if (max_rows && ++nr_rows >= max_rows)
+                               break;
+               }
+
+               hist_group__for_each_entry_out(h, g) {
+                       percent = h->stat.period * 100.0 /
                                        g->entry.stat.period;
 
                        if (h->filtered)
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 32cdc7a..f9735c4 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -786,6 +786,12 @@ out:
        return ret;
 }
 
+static int hist_entry_group__sort_on_period(struct hist_entry_group *a,
+                                           struct hist_entry_group *b)
+{
+       return period_cmp(a->entry.stat.period, b->entry.stat.period);
+}
+
 static void __hists__insert_output_entry(struct rb_root *entries,
                                         struct hist_entry *he,
                                         u64 min_callchain_hits)
@@ -818,6 +824,17 @@ static void __hists__insert_output_entry_group(struct 
rb_root *groups,
 {
        struct rb_node **p = &groups->rb_node;
        struct rb_node *parent = NULL;
+       struct hist_entry_group *iter;
+
+       while (*p != NULL) {
+               parent = *p;
+               iter = rb_entry(parent, struct hist_entry_group, rb_node);
+
+               if (hist_entry_group__sort_on_period(hg, iter) > 0)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
+       }
 
        rb_link_node(&hg->rb_node, parent, p);
        rb_insert_color(&hg->rb_node, groups);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 1155397..75a041c 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -71,6 +71,7 @@ enum hist_column {
        HISTC_MEM_TLB,
        HISTC_MEM_LVL,
        HISTC_MEM_SNOOP,
+       HISTC_MEM_CACHELINE,
        HISTC_TRANSACTION,
        HISTC_NR_COLS, /* Last entry */
 };
@@ -106,6 +107,8 @@ int64_t hist_group__collapse(struct hist_entry *left, 
struct hist_entry *right);
 int hist_entry__transaction_len(void);
 int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size,
                              struct hists *hists);
+int hist_entry_group__sort_snprintf(struct hist_entry *he, char *bf, size_t 
size,
+                                   struct hists *hists);
 void hist_entry__free(struct hist_entry *);
 
 void hists__output_resort(struct hists *hists);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index c292c78..bfb4a668 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -8,6 +8,8 @@ const char      default_parent_pattern[] = 
"^sys_|^do_page_fault";
 const char     *parent_pattern = default_parent_pattern;
 const char     default_sort_order[] = "comm,dso,symbol";
 const char     *sort_order = default_sort_order;
+const char     default_group_sort_order[] = "";
+const char     *group_sort_order = default_group_sort_order;
 regex_t                ignore_callees_regex;
 int            have_ignore_callees = 0;
 int            sort__need_collapse = 0;
@@ -61,7 +63,7 @@ static int64_t cmp_null(const void *l, const void *r)
 static int64_t
 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
 {
-       return right->thread->tid - left->thread->tid;
+       return right->thread->pid_ - left->thread->pid_;
 }
 
 static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
@@ -809,6 +811,94 @@ static int hist_entry__global_weight_snprintf(struct 
hist_entry *he, char *bf,
        return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
 }
 
+#define L1_CACHE_BYTES 64
+#define CACHELINE_MASK (L1_CACHE_BYTES - 1)
+#define CL(x)  (x & ~CACHELINE_MASK)
+
+static int64_t
+sort__cacheline_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+       u64 l, r;
+       struct map *l_map = left->mem_info->daddr.map;
+       struct map *r_map = right->mem_info->daddr.map;
+
+       /* store all NULL mem maps at the bottom */
+       /* shouldn't even need this check, should have stubs */
+       if (!left->mem_info->daddr.map || !right->mem_info->daddr.map)
+               return 1;
+
+       /* group event types together */
+       if (left->cpumode > right->cpumode) return -1;
+       if (left->cpumode < right->cpumode) return 1;
+
+       /*
+        * Addresses with no major/minor numbers are assumed to be
+        * anonymous in userspace.  Sort those on pid then address.
+        *
+        * The kernel and non-zero major/minor mapped areas are
+        * assumed to be unity mapped.  Sort those on address then pid.
+        */
+
+       if ((l_map->maj || l_map->min || l_map->ino || l_map-> ino_generation)) 
{
+               /* mmapped areas */
+
+               if (l_map->maj > r_map->maj) return -1;
+               if (l_map->maj < r_map->maj) return 1;
+
+               if (l_map->min > r_map->min) return -1;
+               if (l_map->min < r_map->min) return 1;
+
+               if (l_map->ino > r_map->ino) return -1;
+               if (l_map->ino < r_map->ino) return 1;
+
+               if (l_map->ino_generation > r_map->ino_generation) return -1;
+               if (l_map->ino_generation < r_map->ino_generation) return 1;
+
+       } else if (left->cpumode != PERF_RECORD_MISC_KERNEL) {
+               /* userspace anonymous */
+               if (left->thread->pid_ > right->thread->pid_) return -1;
+               if (left->thread->pid_ < right->thread->pid_) return 1;
+       }
+
+       /* al_addr does all the right addr - start + offset calculations */
+       l = left->mem_info->daddr.al_addr;
+       r = right->mem_info->daddr.al_addr;
+
+       if (CL(l) > CL(r)) return -1;
+       if (CL(l) < CL(r)) return 1;
+               
+       /* sanity check the maps; only mmaped areas should have different maps 
*/
+       if ((left->mem_info->daddr.map != right->mem_info->daddr.map) &&
+            !right->mem_info->daddr.map->maj && 
!right->mem_info->daddr.map->min)
+               pr_debug("mem_cacheline_cmp: Similar entries have different 
maps\n");
+
+       return 0;
+}
+
+static int hist_entry__cacheline_snprintf(struct hist_entry *he, char *bf,
+                                           size_t size, unsigned int width)
+{
+       uint64_t addr = 0;
+       struct map *map = NULL;
+       struct symbol *sym = NULL;
+       char level = he->level;
+
+       if (he->mem_info) {
+               addr = CL(he->mem_info->daddr.al_addr);
+               map = he->mem_info->daddr.map;
+               sym = he->mem_info->daddr.sym;
+
+               /* print [s] for data mmaps */
+               if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
+                    map && (map->type == MAP__VARIABLE) &&
+                   (map->maj || map->min || map->ino ||
+                    map-> ino_generation))
+                       level = 's';
+       }
+
+       return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
+                                        width);
+}
 struct sort_entry sort_global_weight = {
        .se_header      = "Weight",
        .se_cmp         = sort__global_weight_cmp,
@@ -858,6 +948,13 @@ struct sort_entry sort_mem_snoop = {
        .se_width_idx   = HISTC_MEM_SNOOP,
 };
 
+struct sort_entry sort_mem_cacheline = {
+       .se_header      = "Cacheline",
+       .se_cmp         = sort__cacheline_cmp,
+       .se_snprintf    = hist_entry__cacheline_snprintf,
+       .se_width_idx   = HISTC_MEM_CACHELINE,
+};
+
 static int64_t
 sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
 {
@@ -1000,6 +1097,11 @@ static struct sort_dimension common_sort_dimensions[] = {
        DIM(SORT_TRANSACTION, "transaction", sort_transaction),
 };
 
+static struct sort_dimension common_group_sort_dimensions[] = {
+       DIM(0, "pid", sort_thread),
+       DIM(1, "cpu", sort_cpu),
+};
+
 #undef DIM
 
 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = 
&(func) }
@@ -1027,6 +1129,12 @@ static struct sort_dimension memory_sort_dimensions[] = {
        DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
 };
 
+static struct sort_dimension memory_group_sort_dimensions[] = {
+       DIM(0 + __SORT_MEMORY_MODE, "cacheline", sort_mem_cacheline),
+       DIM(1 + __SORT_MEMORY_MODE, "mem", sort_mem_lvl),
+       DIM(2 + __SORT_MEMORY_MODE, "snoop", sort_mem_snoop),
+};
+
 #undef DIM
 
 static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type 
idx)
@@ -1109,12 +1217,81 @@ int sort_dimension__add(const char *tok)
        return -ESRCH;
 }
 
+static void __sort_dimension__add_group(struct sort_dimension *sd, enum 
sort_type idx)
+{
+       if (sd->taken)
+               return;
+
+       if (sd->entry->se_collapse)
+               sort__need_collapse = 1;
+
+       if (list_empty(&hist_group__sort_list))
+               sort__first_dimension = idx;
+
+       list_add_tail(&sd->entry->list, &hist_group__sort_list);
+       sd->taken = 1;
+}
+
+int sort_dimension__add_group(const char *tok)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(common_group_sort_dimensions); i++) {
+               struct sort_dimension *sd = &common_group_sort_dimensions[i];
+
+               if (strncasecmp(tok, sd->name, strlen(tok)))
+                       continue;
+
+               sort__has_group = 1;
+
+               __sort_dimension__add_group(sd, i);
+               return 0;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
+               struct sort_dimension *sd = &memory_group_sort_dimensions[i];
+
+               if (strncasecmp(tok, sd->name, strlen(tok)))
+                       continue;
+
+               if (sort__mode != SORT_MODE__MEMORY)
+                       return -EINVAL;
+
+               sort__has_group = 1;
+
+               __sort_dimension__add_group(sd, i + __SORT_MEMORY_MODE);
+               return 0;
+       }
+
+       return -ESRCH;
+}
+
 int setup_sorting(void)
 {
-       char *tmp, *tok, *str = strdup(sort_order);
+       char *tmp, *tok, *str = strdup(group_sort_order);
        int ret = 0;
 
        if (str == NULL) {
+               error("Not enough memory to setup group sort keys");
+               return -ENOMEM;
+       }
+
+       for (tok = strtok_r(str, ", ", &tmp);
+                       tok; tok = strtok_r(NULL, ", ", &tmp)) {
+               ret = sort_dimension__add_group(tok);
+               if (ret == -EINVAL) {
+                       error("Invalid --sort key: `%s'", tok);
+                       break;
+               } else if (ret == -ESRCH) {
+                       error("Unknown --sort key: `%s'", tok);
+                       break;
+               }
+       }
+
+       free(str);
+       str = strdup(sort_order);
+
+       if (str == NULL) {
                error("Not enough memory to setup sort keys");
                return -ENOMEM;
        }
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index ff24050..ad5001f 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -26,9 +26,11 @@
 
 extern regex_t parent_regex;
 extern const char *sort_order;
+extern const char *group_sort_order;
 extern const char default_parent_pattern[];
 extern const char *parent_pattern;
 extern const char default_sort_order[];
+extern const char default_group_sort_order[];
 extern regex_t ignore_callees_regex;
 extern int have_ignore_callees;
 extern int sort__need_collapse;
@@ -91,6 +93,7 @@ struct hist_entry {
        u64                     ip;
        u64                     transaction;
        s32                     cpu;
+       u8                      cpumode;
 
        struct hist_entry_diff  diff;
 
@@ -322,6 +325,7 @@ extern struct list_head hist_group__sort_list;
 
 int setup_sorting(void);
 extern int sort_dimension__add(const char *);
+extern int sort_dimension__add_group(const char *);
 void sort__setup_elide(FILE *fp);
 
 int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, 
int unset);
-- 
1.7.11.7

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to