Commit b5a80b7e authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'perf' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core

parents 84b13fd5 f6c903f5
...@@ -40,7 +40,9 @@ Synopsis of kprobe_events ...@@ -40,7 +40,9 @@ Synopsis of kprobe_events
$stack : Fetch stack address. $stack : Fetch stack address.
$retval : Fetch return value.(*) $retval : Fetch return value.(*)
+|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**) +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
NAME=FETCHARG: Set NAME as the argument name of FETCHARG. NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
(u8/u16/u32/u64/s8/s16/s32/s64) are supported.
(*) only for return probe. (*) only for return probe.
(**) this is useful for fetching a field of data structures. (**) this is useful for fetching a field of data structures.
......
...@@ -102,29 +102,17 @@ struct syscall_trace_exit { ...@@ -102,29 +102,17 @@ struct syscall_trace_exit {
long ret; long ret;
}; };
struct kprobe_trace_entry { struct kprobe_trace_entry_head {
struct trace_entry ent; struct trace_entry ent;
unsigned long ip; unsigned long ip;
int nargs;
unsigned long args[];
}; };
#define SIZEOF_KPROBE_TRACE_ENTRY(n) \ struct kretprobe_trace_entry_head {
(offsetof(struct kprobe_trace_entry, args) + \
(sizeof(unsigned long) * (n)))
struct kretprobe_trace_entry {
struct trace_entry ent; struct trace_entry ent;
unsigned long func; unsigned long func;
unsigned long ret_ip; unsigned long ret_ip;
int nargs;
unsigned long args[];
}; };
#define SIZEOF_KRETPROBE_TRACE_ENTRY(n) \
(offsetof(struct kretprobe_trace_entry, args) + \
(sizeof(unsigned long) * (n)))
/* /*
* trace_flag_type is an enumeration that holds different * trace_flag_type is an enumeration that holds different
* states when a trace occurs. These are: * states when a trace occurs. These are:
......
This diff is collapsed.
...@@ -79,7 +79,16 @@ Probe points are defined by following syntax. ...@@ -79,7 +79,16 @@ Probe points are defined by following syntax.
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'. 'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function. 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern. It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc). 'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
PROBE ARGUMENT
--------------
Each probe argument follows below syntax.
[NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo.
LINE SYNTAX LINE SYNTAX
----------- -----------
......
...@@ -40,7 +40,6 @@ ...@@ -40,7 +40,6 @@
#include "util/debug.h" #include "util/debug.h"
#include "util/debugfs.h" #include "util/debugfs.h"
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/parse-events.h" /* For debugfs_path */
#include "util/probe-finder.h" #include "util/probe-finder.h"
#include "util/probe-event.h" #include "util/probe-event.h"
...@@ -59,23 +58,25 @@ static struct { ...@@ -59,23 +58,25 @@ static struct {
/* Parse an event definition. Note that any error must die. */ /* Parse an event definition. Note that any error must die. */
static void parse_probe_event(const char *str) static int parse_probe_event(const char *str)
{ {
struct perf_probe_event *pev = &params.events[params.nevents]; struct perf_probe_event *pev = &params.events[params.nevents];
int ret;
pr_debug("probe-definition(%d): %s\n", params.nevents, str); pr_debug("probe-definition(%d): %s\n", params.nevents, str);
if (++params.nevents == MAX_PROBES) if (++params.nevents == MAX_PROBES)
die("Too many probes (> %d) are specified.", MAX_PROBES); die("Too many probes (> %d) are specified.", MAX_PROBES);
/* Parse a perf-probe command into event */ /* Parse a perf-probe command into event */
parse_perf_probe_command(str, pev); ret = parse_perf_probe_command(str, pev);
pr_debug("%d arguments\n", pev->nargs); pr_debug("%d arguments\n", pev->nargs);
return ret;
} }
static void parse_probe_event_argv(int argc, const char **argv) static int parse_probe_event_argv(int argc, const char **argv)
{ {
int i, len; int i, len, ret;
char *buf; char *buf;
/* Bind up rest arguments */ /* Bind up rest arguments */
...@@ -86,16 +87,18 @@ static void parse_probe_event_argv(int argc, const char **argv) ...@@ -86,16 +87,18 @@ static void parse_probe_event_argv(int argc, const char **argv)
len = 0; len = 0;
for (i = 0; i < argc; i++) for (i = 0; i < argc; i++)
len += sprintf(&buf[len], "%s ", argv[i]); len += sprintf(&buf[len], "%s ", argv[i]);
parse_probe_event(buf); ret = parse_probe_event(buf);
free(buf); free(buf);
return ret;
} }
static int opt_add_probe_event(const struct option *opt __used, static int opt_add_probe_event(const struct option *opt __used,
const char *str, int unset __used) const char *str, int unset __used)
{ {
if (str) if (str)
parse_probe_event(str); return parse_probe_event(str);
return 0; else
return 0;
} }
static int opt_del_probe_event(const struct option *opt __used, static int opt_del_probe_event(const struct option *opt __used,
...@@ -113,11 +116,14 @@ static int opt_del_probe_event(const struct option *opt __used, ...@@ -113,11 +116,14 @@ static int opt_del_probe_event(const struct option *opt __used,
static int opt_show_lines(const struct option *opt __used, static int opt_show_lines(const struct option *opt __used,
const char *str, int unset __used) const char *str, int unset __used)
{ {
int ret = 0;
if (str) if (str)
parse_line_range_desc(str, &params.line_range); ret = parse_line_range_desc(str, &params.line_range);
INIT_LIST_HEAD(&params.line_range.line_list); INIT_LIST_HEAD(&params.line_range.line_list);
params.show_lines = true; params.show_lines = true;
return 0;
return ret;
} }
#endif #endif
...@@ -142,9 +148,9 @@ static const struct option options[] = { ...@@ -142,9 +148,9 @@ static const struct option options[] = {
OPT_CALLBACK('a', "add", NULL, OPT_CALLBACK('a', "add", NULL,
#ifdef DWARF_SUPPORT #ifdef DWARF_SUPPORT
"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
" [ARG ...]", " [[NAME=]ARG ...]",
#else #else
"[EVENT=]FUNC[+OFF|%return] [ARG ...]", "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]",
#endif #endif
"probe point definition, where\n" "probe point definition, where\n"
"\t\tGROUP:\tGroup name (optional)\n" "\t\tGROUP:\tGroup name (optional)\n"
...@@ -178,6 +184,8 @@ static const struct option options[] = { ...@@ -178,6 +184,8 @@ static const struct option options[] = {
int cmd_probe(int argc, const char **argv, const char *prefix __used) int cmd_probe(int argc, const char **argv, const char *prefix __used)
{ {
int ret;
argc = parse_options(argc, argv, options, probe_usage, argc = parse_options(argc, argv, options, probe_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
if (argc > 0) { if (argc > 0) {
...@@ -185,28 +193,31 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -185,28 +193,31 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
pr_warning(" Error: '-' is not supported.\n"); pr_warning(" Error: '-' is not supported.\n");
usage_with_options(probe_usage, options); usage_with_options(probe_usage, options);
} }
parse_probe_event_argv(argc, argv); ret = parse_probe_event_argv(argc, argv);
if (ret < 0) {
pr_err(" Error: Parse Error. (%d)\n", ret);
return ret;
}
} }
if ((!params.nevents && !params.dellist && !params.list_events && if ((!params.nevents && !params.dellist && !params.list_events &&
!params.show_lines)) !params.show_lines))
usage_with_options(probe_usage, options); usage_with_options(probe_usage, options);
if (debugfs_valid_mountpoint(debugfs_path) < 0)
die("Failed to find debugfs path.");
if (params.list_events) { if (params.list_events) {
if (params.nevents != 0 || params.dellist) { if (params.nevents != 0 || params.dellist) {
pr_warning(" Error: Don't use --list with" pr_err(" Error: Don't use --list with --add/--del.\n");
" --add/--del.\n");
usage_with_options(probe_usage, options); usage_with_options(probe_usage, options);
} }
if (params.show_lines) { if (params.show_lines) {
pr_warning(" Error: Don't use --list with --line.\n"); pr_err(" Error: Don't use --list with --line.\n");
usage_with_options(probe_usage, options); usage_with_options(probe_usage, options);
} }
show_perf_probe_events(); ret = show_perf_probe_events();
return 0; if (ret < 0)
pr_err(" Error: Failed to show event list. (%d)\n",
ret);
return ret;
} }
#ifdef DWARF_SUPPORT #ifdef DWARF_SUPPORT
...@@ -217,19 +228,30 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -217,19 +228,30 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
usage_with_options(probe_usage, options); usage_with_options(probe_usage, options);
} }
show_line_range(&params.line_range); ret = show_line_range(&params.line_range);
return 0; if (ret < 0)
pr_err(" Error: Failed to show lines. (%d)\n", ret);
return ret;
} }
#endif #endif
if (params.dellist) { if (params.dellist) {
del_perf_probe_events(params.dellist); ret = del_perf_probe_events(params.dellist);
strlist__delete(params.dellist); strlist__delete(params.dellist);
if (params.nevents == 0) if (ret < 0) {
return 0; pr_err(" Error: Failed to delete events. (%d)\n", ret);
return ret;
}
} }
add_perf_probe_events(params.events, params.nevents, params.force_add); if (params.nevents) {
ret = add_perf_probe_events(params.events, params.nevents,
params.force_add);
if (ret < 0) {
pr_err(" Error: Failed to add events. (%d)\n", ret);
return ret;
}
}
return 0; return 0;
} }
...@@ -68,7 +68,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) ...@@ -68,7 +68,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
int64_t cmp = 0; int64_t cmp = 0;
list_for_each_entry(se, &hist_entry__sort_list, list) { list_for_each_entry(se, &hist_entry__sort_list, list) {
cmp = se->cmp(left, right); cmp = se->se_cmp(left, right);
if (cmp) if (cmp)
break; break;
} }
...@@ -85,7 +85,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) ...@@ -85,7 +85,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
list_for_each_entry(se, &hist_entry__sort_list, list) { list_for_each_entry(se, &hist_entry__sort_list, list) {
int64_t (*f)(struct hist_entry *, struct hist_entry *); int64_t (*f)(struct hist_entry *, struct hist_entry *);
f = se->collapse ?: se->cmp; f = se->se_collapse ?: se->se_cmp;
cmp = f(left, right); cmp = f(left, right);
if (cmp) if (cmp)
...@@ -536,8 +536,8 @@ int hist_entry__snprintf(struct hist_entry *self, ...@@ -536,8 +536,8 @@ int hist_entry__snprintf(struct hist_entry *self,
continue; continue;
ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
ret += se->snprintf(self, s + ret, size - ret, ret += se->se_snprintf(self, s + ret, size - ret,
se->width ? *se->width : 0); se->se_width ? *se->se_width : 0);
} }
return ret; return ret;
...@@ -564,7 +564,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, ...@@ -564,7 +564,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp,
if (sort__first_dimension == SORT_COMM) { if (sort__first_dimension == SORT_COMM) {
struct sort_entry *se = list_first_entry(&hist_entry__sort_list, struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
typeof(*se), list); typeof(*se), list);
left_margin = se->width ? *se->width : 0; left_margin = se->se_width ? *se->se_width : 0;
left_margin -= thread__comm_len(self->thread); left_margin -= thread__comm_len(self->thread);
} }
...@@ -615,22 +615,22 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, ...@@ -615,22 +615,22 @@ size_t perf_session__fprintf_hists(struct rb_root *hists,
if (se->elide) if (se->elide)
continue; continue;
if (sep) { if (sep) {
fprintf(fp, "%c%s", *sep, se->header); fprintf(fp, "%c%s", *sep, se->se_header);
continue; continue;
} }
width = strlen(se->header); width = strlen(se->se_header);
if (se->width) { if (se->se_width) {
if (symbol_conf.col_width_list_str) { if (symbol_conf.col_width_list_str) {
if (col_width) { if (col_width) {
*se->width = atoi(col_width); *se->se_width = atoi(col_width);
col_width = strchr(col_width, ','); col_width = strchr(col_width, ',');
if (col_width) if (col_width)
++col_width; ++col_width;
} }
} }
width = *se->width = max(*se->width, width); width = *se->se_width = max(*se->se_width, width);
} }
fprintf(fp, " %*s", width, se->header); fprintf(fp, " %*s", width, se->se_header);
} }
fprintf(fp, "\n"); fprintf(fp, "\n");
...@@ -652,10 +652,10 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, ...@@ -652,10 +652,10 @@ size_t perf_session__fprintf_hists(struct rb_root *hists,
continue; continue;
fprintf(fp, " "); fprintf(fp, " ");
if (se->width) if (se->se_width)
width = *se->width; width = *se->se_width;
else else
width = strlen(se->header); width = strlen(se->se_header);
for (i = 0; i < width; i++) for (i = 0; i < width; i++)
fprintf(fp, "."); fprintf(fp, ".");
} }
......
This diff is collapsed.
...@@ -23,6 +23,7 @@ struct kprobe_trace_arg_ref { ...@@ -23,6 +23,7 @@ struct kprobe_trace_arg_ref {
struct kprobe_trace_arg { struct kprobe_trace_arg {
char *name; /* Argument name */ char *name; /* Argument name */
char *value; /* Base value */ char *value; /* Base value */
char *type; /* Type name */
struct kprobe_trace_arg_ref *ref; /* Referencing offset */ struct kprobe_trace_arg_ref *ref; /* Referencing offset */
}; };
...@@ -55,6 +56,8 @@ struct perf_probe_arg_field { ...@@ -55,6 +56,8 @@ struct perf_probe_arg_field {
/* Perf probe probing argument */ /* Perf probe probing argument */
struct perf_probe_arg { struct perf_probe_arg {
char *name; /* Argument name */ char *name; /* Argument name */
char *var; /* Variable name */
char *type; /* Type name */
struct perf_probe_arg_field *field; /* Structure fields */ struct perf_probe_arg_field *field; /* Structure fields */
}; };
...@@ -71,25 +74,25 @@ struct perf_probe_event { ...@@ -71,25 +74,25 @@ struct perf_probe_event {
/* Line number container */ /* Line number container */
struct line_node { struct line_node {
struct list_head list; struct list_head list;
unsigned int line; int line;
}; };
/* Line range */ /* Line range */
struct line_range { struct line_range {
char *file; /* File name */ char *file; /* File name */
char *function; /* Function name */ char *function; /* Function name */
unsigned int start; /* Start line number */ int start; /* Start line number */
unsigned int end; /* End line number */ int end; /* End line number */
int offset; /* Start line offset */ int offset; /* Start line offset */
char *path; /* Real path name */ char *path; /* Real path name */
struct list_head line_list; /* Visible lines */ struct list_head line_list; /* Visible lines */
}; };
/* Command string to events */ /* Command string to events */
extern void parse_perf_probe_command(const char *cmd, extern int parse_perf_probe_command(const char *cmd,
struct perf_probe_event *pev); struct perf_probe_event *pev);
extern void parse_kprobe_trace_command(const char *cmd, extern int parse_kprobe_trace_command(const char *cmd,
struct kprobe_trace_event *tev); struct kprobe_trace_event *tev);
/* Events to command string */ /* Events to command string */
extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
...@@ -101,22 +104,22 @@ extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, ...@@ -101,22 +104,22 @@ extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf,
extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
/* Convert from kprobe_trace_event to perf_probe_event */ /* Convert from kprobe_trace_event to perf_probe_event */
extern void convert_to_perf_probe_event(struct kprobe_trace_event *tev, extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
struct perf_probe_event *pev); struct perf_probe_event *pev);
/* Release event contents */ /* Release event contents */
extern void clear_perf_probe_event(struct perf_probe_event *pev); extern void clear_perf_probe_event(struct perf_probe_event *pev);
extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev);
/* Command string to line-range */ /* Command string to line-range */
extern void parse_line_range_desc(const char *cmd, struct line_range *lr); extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
extern void add_perf_probe_events(struct perf_probe_event *pevs, int ntevs, extern int add_perf_probe_events(struct perf_probe_event *pevs, int ntevs,
bool force_add); bool force_add);
extern void del_perf_probe_events(struct strlist *dellist); extern int del_perf_probe_events(struct strlist *dellist);
extern void show_perf_probe_events(void); extern int show_perf_probe_events(void);
extern void show_line_range(struct line_range *lr); extern int show_line_range(struct line_range *lr);
/* Maximum index number of event-name postfix */ /* Maximum index number of event-name postfix */
......
This diff is collapsed.
...@@ -42,6 +42,7 @@ struct probe_finder { ...@@ -42,6 +42,7 @@ struct probe_finder {
struct list_head lcache; /* Line cache for lazy match */ struct list_head lcache; /* Line cache for lazy match */
/* For variable searching */ /* For variable searching */
Dwarf_CFI *cfi; /* Call Frame Information */
Dwarf_Op *fb_ops; /* Frame base attribute */ Dwarf_Op *fb_ops; /* Frame base attribute */
struct perf_probe_arg *pvar; /* Current target variable */ struct perf_probe_arg *pvar; /* Current target variable */
struct kprobe_trace_arg *tvar; /* Current result variable */ struct kprobe_trace_arg *tvar; /* Current result variable */
......
...@@ -30,38 +30,38 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, ...@@ -30,38 +30,38 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
size_t size, unsigned int width); size_t size, unsigned int width);
struct sort_entry sort_thread = { struct sort_entry sort_thread = {
.header = "Command: Pid", .se_header = "Command: Pid",
.cmp = sort__thread_cmp, .se_cmp = sort__thread_cmp,
.snprintf = hist_entry__thread_snprintf, .se_snprintf = hist_entry__thread_snprintf,
.width = &threads__col_width, .se_width = &threads__col_width,
}; };
struct sort_entry sort_comm = { struct sort_entry sort_comm = {
.header = "Command", .se_header = "Command",
.cmp = sort__comm_cmp, .se_cmp = sort__comm_cmp,
.collapse = sort__comm_collapse, .se_collapse = sort__comm_collapse,
.snprintf = hist_entry__comm_snprintf, .se_snprintf = hist_entry__comm_snprintf,
.width = &comms__col_width, .se_width = &comms__col_width,
}; };
struct sort_entry sort_dso = { struct sort_entry sort_dso = {
.header = "Shared Object", .se_header = "Shared Object",
.cmp = sort__dso_cmp, .se_cmp = sort__dso_cmp,
.snprintf = hist_entry__dso_snprintf, .se_snprintf = hist_entry__dso_snprintf,
.width = &dsos__col_width, .se_width = &dsos__col_width,
}; };
struct sort_entry sort_sym = { struct sort_entry sort_sym = {
.header = "Symbol", .se_header = "Symbol",
.cmp = sort__sym_cmp, .se_cmp = sort__sym_cmp,
.snprintf = hist_entry__sym_snprintf, .se_snprintf = hist_entry__sym_snprintf,
}; };
struct sort_entry sort_parent = { struct sort_entry sort_parent = {
.header = "Parent symbol", .se_header = "Parent symbol",
.cmp = sort__parent_cmp, .se_cmp = sort__parent_cmp,
.snprintf = hist_entry__parent_snprintf, .se_snprintf = hist_entry__parent_snprintf,
.width = &parent_symbol__col_width, .se_width = &parent_symbol__col_width,
}; };
struct sort_dimension { struct sort_dimension {
...@@ -255,7 +255,7 @@ int sort_dimension__add(const char *tok) ...@@ -255,7 +255,7 @@ int sort_dimension__add(const char *tok)
if (strncasecmp(tok, sd->name, strlen(tok))) if (strncasecmp(tok, sd->name, strlen(tok)))
continue; continue;
if (sd->entry->collapse) if (sd->entry->se_collapse)
sort__need_collapse = 1; sort__need_collapse = 1;
if (sd->entry == &sort_parent) { if (sd->entry == &sort_parent) {
......
...@@ -78,13 +78,13 @@ enum sort_type { ...@@ -78,13 +78,13 @@ enum sort_type {
struct sort_entry { struct sort_entry {
struct list_head list; struct list_head list;
const char *header; const char *se_header;
int64_t (*cmp)(struct hist_entry *, struct hist_entry *); int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
int64_t (*collapse)(struct hist_entry *, struct hist_entry *); int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
int (*snprintf)(struct hist_entry *self, char *bf, size_t size, int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size,
unsigned int width); unsigned int width);
unsigned int *width; unsigned int *se_width;
bool elide; bool elide;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment