sort.c 8.3 KB
Newer Older
1
#include "sort.h"
2
#include "hist.h"
3 4

regex_t		parent_regex;
5 6 7 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;
9 10
int		sort__need_collapse = 0;
int		sort__has_parent = 0;
11 12

enum sort_type	sort__first_dimension;
13 14 15 16 17

char * field_sep;

LIST_HEAD(hist_entry__sort_list);

18 19 20 21 22 23 24 25 26 27
static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
				       size_t size, unsigned int width);
static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
				     size_t size, unsigned int width);
static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
				    size_t size, unsigned int width);
static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
				    size_t size, unsigned int width);
static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
				       size_t size, unsigned int width);
A
Arun Sharma 已提交
28 29
static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
				    size_t size, unsigned int width);
30

31
struct sort_entry sort_thread = {
32 33 34
	.se_header	= "Command:  Pid",
	.se_cmp		= sort__thread_cmp,
	.se_snprintf	= hist_entry__thread_snprintf,
35
	.se_width_idx	= HISTC_THREAD,
36 37 38
};

struct sort_entry sort_comm = {
39 40 41 42
	.se_header	= "Command",
	.se_cmp		= sort__comm_cmp,
	.se_collapse	= sort__comm_collapse,
	.se_snprintf	= hist_entry__comm_snprintf,
43
	.se_width_idx	= HISTC_COMM,
44 45 46
};

struct sort_entry sort_dso = {
47 48 49
	.se_header	= "Shared Object",
	.se_cmp		= sort__dso_cmp,
	.se_snprintf	= hist_entry__dso_snprintf,
50
	.se_width_idx	= HISTC_DSO,
51 52 53
};

struct sort_entry sort_sym = {
54 55 56
	.se_header	= "Symbol",
	.se_cmp		= sort__sym_cmp,
	.se_snprintf	= hist_entry__sym_snprintf,
57
	.se_width_idx	= HISTC_SYMBOL,
58 59 60
};

struct sort_entry sort_parent = {
61 62 63
	.se_header	= "Parent symbol",
	.se_cmp		= sort__parent_cmp,
	.se_snprintf	= hist_entry__parent_snprintf,
64
	.se_width_idx	= HISTC_PARENT,
65
};
A
Arun Sharma 已提交
66 67 68 69 70
 
struct sort_entry sort_cpu = {
	.se_header      = "CPU",
	.se_cmp	        = sort__cpu_cmp,
	.se_snprintf    = hist_entry__cpu_snprintf,
71
	.se_width_idx	= HISTC_CPU,
A
Arun Sharma 已提交
72
};
73 74 75 76 77 78 79 80 81 82 83 84 85

struct sort_dimension {
	const char		*name;
	struct sort_entry	*entry;
	int			taken;
};

static struct sort_dimension sort_dimensions[] = {
	{ .name = "pid",	.entry = &sort_thread,	},
	{ .name = "comm",	.entry = &sort_comm,	},
	{ .name = "dso",	.entry = &sort_dso,	},
	{ .name = "symbol",	.entry = &sort_sym,	},
	{ .name = "parent",	.entry = &sort_parent,	},
A
Arun Sharma 已提交
86
	{ .name = "cpu",	.entry = &sort_cpu,	},
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
};

int64_t cmp_null(void *l, void *r)
{
	if (!l && !r)
		return 0;
	else if (!l)
		return -1;
	else
		return 1;
}

/* --sort pid */

int64_t
sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return right->thread->pid - left->thread->pid;
}

107
static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
108 109 110 111 112
{
	int n;
	va_list ap;

	va_start(ap, fmt);
113 114 115 116 117 118 119 120 121
	n = vsnprintf(bf, size, fmt, ap);
	if (field_sep && n > 0) {
		char *sep = bf;

		while (1) {
			sep = strchr(sep, *field_sep);
			if (sep == NULL)
				break;
			*sep = '.';
122 123 124 125 126 127
		}
	}
	va_end(ap);
	return n;
}

128 129
static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
				       size_t size, unsigned int width)
130
{
131
	return repsep_snprintf(bf, size, "%*s:%5d", width,
132 133 134
			      self->thread->comm ?: "", self->thread->pid);
}

135 136
static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
				     size_t size, unsigned int width)
137
{
138
	return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
139 140 141 142 143 144 145
}

/* --sort dso */

int64_t
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
{
146 147
	struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
	struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL;
148
	const char *dso_name_l, *dso_name_r;
149 150 151 152

	if (!dso_l || !dso_r)
		return cmp_null(dso_l, dso_r);

153 154 155 156 157 158 159 160 161
	if (verbose) {
		dso_name_l = dso_l->long_name;
		dso_name_r = dso_r->long_name;
	} else {
		dso_name_l = dso_l->short_name;
		dso_name_r = dso_r->short_name;
	}

	return strcmp(dso_name_l, dso_name_r);
162 163
}

164 165
static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
				    size_t size, unsigned int width)
166
{
167 168 169
	if (self->ms.map && self->ms.map->dso) {
		const char *dso_name = !verbose ? self->ms.map->dso->short_name :
						  self->ms.map->dso->long_name;
170
		return repsep_snprintf(bf, size, "%-*s", width, dso_name);
171
	}
172

173
	return repsep_snprintf(bf, size, "%*Lx", width, self->ip);
174 175 176 177 178 179 180 181 182
}

/* --sort symbol */

int64_t
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
{
	u64 ip_l, ip_r;

183
	if (left->ms.sym == right->ms.sym)
184 185
		return 0;

186 187
	ip_l = left->ms.sym ? left->ms.sym->start : left->ip;
	ip_r = right->ms.sym ? right->ms.sym->start : right->ip;
188 189 190 191

	return (int64_t)(ip_r - ip_l);
}

192 193
static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
				    size_t size, unsigned int width __used)
194 195 196
{
	size_t ret = 0;

197
	if (verbose) {
198
		char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
199 200
		ret += repsep_snprintf(bf, size, "%*Lx %c ",
				       BITS_PER_LONG / 4, self->ip, o);
201
	}
202

203
	ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
204
	if (self->ms.sym)
205 206
		ret += repsep_snprintf(bf + ret, size - ret, "%s",
				       self->ms.sym->name);
207
	else
208 209
		ret += repsep_snprintf(bf + ret, size - ret, "%*Lx",
				       BITS_PER_LONG / 4, self->ip);
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

	return ret;
}

/* --sort comm */

int64_t
sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return right->thread->pid - left->thread->pid;
}

int64_t
sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
{
	char *comm_l = left->thread->comm;
	char *comm_r = right->thread->comm;

	if (!comm_l || !comm_r)
		return cmp_null(comm_l, comm_r);

	return strcmp(comm_l, comm_r);
}

/* --sort parent */

int64_t
sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
{
	struct symbol *sym_l = left->parent;
	struct symbol *sym_r = right->parent;

	if (!sym_l || !sym_r)
		return cmp_null(sym_l, sym_r);

	return strcmp(sym_l->name, sym_r->name);
}

248 249
static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
				       size_t size, unsigned int width)
250
{
251
	return repsep_snprintf(bf, size, "%-*s", width,
252 253 254
			      self->parent ? self->parent->name : "[other]");
}

A
Arun Sharma 已提交
255 256 257 258 259 260 261 262 263 264 265 266 267 268
/* --sort cpu */

int64_t
sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
{
	return right->cpu - left->cpu;
}

static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
				       size_t size, unsigned int width)
{
	return repsep_snprintf(bf, size, "%-*d", width, self->cpu);
}

269 270 271 272 273 274 275 276 277 278 279 280 281
int sort_dimension__add(const char *tok)
{
	unsigned int i;

	for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
		struct sort_dimension *sd = &sort_dimensions[i];

		if (sd->taken)
			continue;

		if (strncasecmp(tok, sd->name, strlen(tok)))
			continue;

282
		if (sd->entry->se_collapse)
283 284 285 286 287 288 289 290
			sort__need_collapse = 1;

		if (sd->entry == &sort_parent) {
			int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
			if (ret) {
				char err[BUFSIZ];

				regerror(ret, &parent_regex, err, sizeof(err));
291 292
				pr_err("Invalid regex: %s\n%s", parent_pattern, err);
				return -EINVAL;
293 294 295 296
			}
			sort__has_parent = 1;
		}

297 298 299 300 301 302 303 304 305 306 307
		if (list_empty(&hist_entry__sort_list)) {
			if (!strcmp(sd->name, "pid"))
				sort__first_dimension = SORT_PID;
			else if (!strcmp(sd->name, "comm"))
				sort__first_dimension = SORT_COMM;
			else if (!strcmp(sd->name, "dso"))
				sort__first_dimension = SORT_DSO;
			else if (!strcmp(sd->name, "symbol"))
				sort__first_dimension = SORT_SYM;
			else if (!strcmp(sd->name, "parent"))
				sort__first_dimension = SORT_PARENT;
A
Arun Sharma 已提交
308 309
			else if (!strcmp(sd->name, "cpu"))
				sort__first_dimension = SORT_CPU;
310
		}
311

312 313 314 315 316 317 318 319
		list_add_tail(&sd->entry->list, &hist_entry__sort_list);
		sd->taken = 1;

		return 0;
	}

	return -ESRCH;
}
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334

void setup_sorting(const char * const usagestr[], const struct option *opts)
{
	char *tmp, *tok, *str = strdup(sort_order);

	for (tok = strtok_r(str, ", ", &tmp);
			tok; tok = strtok_r(NULL, ", ", &tmp)) {
		if (sort_dimension__add(tok) < 0) {
			error("Unknown --sort key: `%s'", tok);
			usage_with_options(usagestr, opts);
		}
	}

	free(str);
}
335 336 337 338 339 340 341 342 343 344 345

void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
			     const char *list_name, FILE *fp)
{
	if (list && strlist__nr_entries(list) == 1) {
		if (fp != NULL)
			fprintf(fp, "# %s: %s\n", list_name,
				strlist__entry(list, 0)->s);
		self->elide = true;
	}
}