Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
hist.c
Go to the documentation of this file.
1 #include <stdio.h>
2 
3 #include "../../util/util.h"
4 #include "../../util/hist.h"
5 #include "../../util/sort.h"
6 
7 
8 static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
9 {
10  int i;
11  int ret = fprintf(fp, " ");
12 
13  for (i = 0; i < left_margin; i++)
14  ret += fprintf(fp, " ");
15 
16  return ret;
17 }
18 
19 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
20  int left_margin)
21 {
22  int i;
23  size_t ret = callchain__fprintf_left_margin(fp, left_margin);
24 
25  for (i = 0; i < depth; i++)
26  if (depth_mask & (1 << i))
27  ret += fprintf(fp, "| ");
28  else
29  ret += fprintf(fp, " ");
30 
31  ret += fprintf(fp, "\n");
32 
33  return ret;
34 }
35 
36 static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
37  int depth, int depth_mask, int period,
38  u64 total_samples, u64 hits,
39  int left_margin)
40 {
41  int i;
42  size_t ret = 0;
43 
44  ret += callchain__fprintf_left_margin(fp, left_margin);
45  for (i = 0; i < depth; i++) {
46  if (depth_mask & (1 << i))
47  ret += fprintf(fp, "|");
48  else
49  ret += fprintf(fp, " ");
50  if (!period && i == depth - 1) {
51  double percent;
52 
53  percent = hits * 100.0 / total_samples;
54  ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
55  } else
56  ret += fprintf(fp, "%s", " ");
57  }
58  if (chain->ms.sym)
59  ret += fprintf(fp, "%s\n", chain->ms.sym->name);
60  else
61  ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip);
62 
63  return ret;
64 }
65 
66 static struct symbol *rem_sq_bracket;
67 static struct callchain_list rem_hits;
68 
69 static void init_rem_hits(void)
70 {
71  rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
72  if (!rem_sq_bracket) {
73  fprintf(stderr, "Not enough memory to display remaining hits\n");
74  return;
75  }
76 
77  strcpy(rem_sq_bracket->name, "[...]");
78  rem_hits.ms.sym = rem_sq_bracket;
79 }
80 
81 static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
82  u64 total_samples, int depth,
83  int depth_mask, int left_margin)
84 {
85  struct rb_node *node, *next;
86  struct callchain_node *child;
87  struct callchain_list *chain;
88  int new_depth_mask = depth_mask;
89  u64 remaining;
90  size_t ret = 0;
91  int i;
92  uint entries_printed = 0;
93 
94  remaining = total_samples;
95 
96  node = rb_first(root);
97  while (node) {
98  u64 new_total;
99  u64 cumul;
100 
101  child = rb_entry(node, struct callchain_node, rb_node);
102  cumul = callchain_cumul_hits(child);
103  remaining -= cumul;
104 
105  /*
106  * The depth mask manages the output of pipes that show
107  * the depth. We don't want to keep the pipes of the current
108  * level for the last child of this depth.
109  * Except if we have remaining filtered hits. They will
110  * supersede the last child
111  */
112  next = rb_next(node);
113  if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
114  new_depth_mask &= ~(1 << (depth - 1));
115 
116  /*
117  * But we keep the older depth mask for the line separator
118  * to keep the level link until we reach the last child
119  */
120  ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
121  left_margin);
122  i = 0;
123  list_for_each_entry(chain, &child->val, list) {
124  ret += ipchain__fprintf_graph(fp, chain, depth,
125  new_depth_mask, i++,
126  total_samples,
127  cumul,
128  left_margin);
129  }
130 
132  new_total = child->children_hit;
133  else
134  new_total = total_samples;
135 
136  ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
137  depth + 1,
138  new_depth_mask | (1 << depth),
139  left_margin);
140  node = next;
141  if (++entries_printed == callchain_param.print_limit)
142  break;
143  }
144 
146  remaining && remaining != total_samples) {
147 
148  if (!rem_sq_bracket)
149  return ret;
150 
151  new_depth_mask &= ~(1 << (depth - 1));
152  ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
153  new_depth_mask, 0, total_samples,
154  remaining, left_margin);
155  }
156 
157  return ret;
158 }
159 
160 static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
161  u64 total_samples, int left_margin)
162 {
163  struct callchain_node *cnode;
164  struct callchain_list *chain;
165  u32 entries_printed = 0;
166  bool printed = false;
167  struct rb_node *node;
168  int i = 0;
169  int ret = 0;
170 
171  /*
172  * If have one single callchain root, don't bother printing
173  * its percentage (100 % in fractal mode and the same percentage
174  * than the hist in graph mode). This also avoid one level of column.
175  */
176  node = rb_first(root);
177  if (node && !rb_next(node)) {
178  cnode = rb_entry(node, struct callchain_node, rb_node);
179  list_for_each_entry(chain, &cnode->val, list) {
180  /*
181  * If we sort by symbol, the first entry is the same than
182  * the symbol. No need to print it otherwise it appears as
183  * displayed twice.
184  */
185  if (!i++ && sort__first_dimension == SORT_SYM)
186  continue;
187  if (!printed) {
188  ret += callchain__fprintf_left_margin(fp, left_margin);
189  ret += fprintf(fp, "|\n");
190  ret += callchain__fprintf_left_margin(fp, left_margin);
191  ret += fprintf(fp, "---");
192  left_margin += 3;
193  printed = true;
194  } else
195  ret += callchain__fprintf_left_margin(fp, left_margin);
196 
197  if (chain->ms.sym)
198  ret += fprintf(fp, " %s\n", chain->ms.sym->name);
199  else
200  ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
201 
202  if (++entries_printed == callchain_param.print_limit)
203  break;
204  }
205  root = &cnode->rb_root;
206  }
207 
208  ret += __callchain__fprintf_graph(fp, root, total_samples,
209  1, 1, left_margin);
210  ret += fprintf(fp, "\n");
211 
212  return ret;
213 }
214 
215 static size_t __callchain__fprintf_flat(FILE *fp,
216  struct callchain_node *self,
217  u64 total_samples)
218 {
219  struct callchain_list *chain;
220  size_t ret = 0;
221 
222  if (!self)
223  return 0;
224 
225  ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
226 
227 
228  list_for_each_entry(chain, &self->val, list) {
229  if (chain->ip >= PERF_CONTEXT_MAX)
230  continue;
231  if (chain->ms.sym)
232  ret += fprintf(fp, " %s\n", chain->ms.sym->name);
233  else
234  ret += fprintf(fp, " %p\n",
235  (void *)(long)chain->ip);
236  }
237 
238  return ret;
239 }
240 
241 static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
242  u64 total_samples)
243 {
244  size_t ret = 0;
245  u32 entries_printed = 0;
246  struct rb_node *rb_node;
247  struct callchain_node *chain;
248 
249  rb_node = rb_first(self);
250  while (rb_node) {
251  double percent;
252 
253  chain = rb_entry(rb_node, struct callchain_node, rb_node);
254  percent = chain->hit * 100.0 / total_samples;
255 
256  ret = percent_color_fprintf(fp, " %6.2f%%\n", percent);
257  ret += __callchain__fprintf_flat(fp, chain, total_samples);
258  ret += fprintf(fp, "\n");
259  if (++entries_printed == callchain_param.print_limit)
260  break;
261 
262  rb_node = rb_next(rb_node);
263  }
264 
265  return ret;
266 }
267 
268 static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
269  u64 total_samples, int left_margin,
270  FILE *fp)
271 {
272  switch (callchain_param.mode) {
273  case CHAIN_GRAPH_REL:
274  return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period,
275  left_margin);
276  break;
277  case CHAIN_GRAPH_ABS:
278  return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
279  left_margin);
280  break;
281  case CHAIN_FLAT:
282  return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
283  break;
284  case CHAIN_NONE:
285  break;
286  default:
287  pr_err("Bad callchain mode\n");
288  }
289 
290  return 0;
291 }
292 
293 static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
294  struct hists *hists,
295  FILE *fp)
296 {
297  int left_margin = 0;
298  u64 total_period = hists->stats.total_period;
299 
301  struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
302  typeof(*se), list);
303  left_margin = hists__col_len(hists, se->se_width_idx);
304  left_margin -= thread__comm_len(he->thread);
305  }
306 
307  return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
308 }
309 
310 static int hist_entry__fprintf(struct hist_entry *he, size_t size,
311  struct hists *hists, FILE *fp)
312 {
313  char bf[512];
314  int ret;
315  struct perf_hpp hpp = {
316  .buf = bf,
317  .size = size,
318  };
319  bool color = !symbol_conf.field_sep;
320 
321  if (size == 0 || size > sizeof(bf))
322  size = hpp.size = sizeof(bf);
323 
324  ret = hist_entry__period_snprintf(&hpp, he, color);
325  hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
326 
327  ret = fprintf(fp, "%s\n", bf);
328 
330  ret += hist_entry__callchain_fprintf(he, hists, fp);
331 
332  return ret;
333 }
334 
335 size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
336  int max_cols, FILE *fp)
337 {
338  struct sort_entry *se;
339  struct rb_node *nd;
340  size_t ret = 0;
341  unsigned int width;
342  const char *sep = symbol_conf.field_sep;
343  const char *col_width = symbol_conf.col_width_list_str;
344  int idx, nr_rows = 0;
345  char bf[64];
346  struct perf_hpp dummy_hpp = {
347  .buf = bf,
348  .size = sizeof(bf),
349  };
350  bool first = true;
351 
352  init_rem_hits();
353 
354  if (!show_header)
355  goto print_entries;
356 
357  fprintf(fp, "# ");
358  for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
359  if (!perf_hpp__format[idx].cond)
360  continue;
361 
362  if (!first)
363  fprintf(fp, "%s", sep ?: " ");
364  else
365  first = false;
366 
367  perf_hpp__format[idx].header(&dummy_hpp);
368  fprintf(fp, "%s", bf);
369  }
370 
372  if (se->elide)
373  continue;
374  if (sep) {
375  fprintf(fp, "%c%s", *sep, se->se_header);
376  continue;
377  }
378  width = strlen(se->se_header);
380  if (col_width) {
381  hists__set_col_len(hists, se->se_width_idx,
382  atoi(col_width));
383  col_width = strchr(col_width, ',');
384  if (col_width)
385  ++col_width;
386  }
387  }
388  if (!hists__new_col_len(hists, se->se_width_idx, width))
389  width = hists__col_len(hists, se->se_width_idx);
390  fprintf(fp, " %*s", width, se->se_header);
391  }
392 
393  fprintf(fp, "\n");
394  if (max_rows && ++nr_rows >= max_rows)
395  goto out;
396 
397  if (sep)
398  goto print_entries;
399 
400  first = true;
401 
402  fprintf(fp, "# ");
403  for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
404  unsigned int i;
405 
406  if (!perf_hpp__format[idx].cond)
407  continue;
408 
409  if (!first)
410  fprintf(fp, "%s", sep ?: " ");
411  else
412  first = false;
413 
414  width = perf_hpp__format[idx].width(&dummy_hpp);
415  for (i = 0; i < width; i++)
416  fprintf(fp, ".");
417  }
418 
420  unsigned int i;
421 
422  if (se->elide)
423  continue;
424 
425  fprintf(fp, " ");
426  width = hists__col_len(hists, se->se_width_idx);
427  if (width == 0)
428  width = strlen(se->se_header);
429  for (i = 0; i < width; i++)
430  fprintf(fp, ".");
431  }
432 
433  fprintf(fp, "\n");
434  if (max_rows && ++nr_rows >= max_rows)
435  goto out;
436 
437  fprintf(fp, "#\n");
438  if (max_rows && ++nr_rows >= max_rows)
439  goto out;
440 
441 print_entries:
442  for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
443  struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
444 
445  if (h->filtered)
446  continue;
447 
448  ret += hist_entry__fprintf(h, max_cols, hists, fp);
449 
450  if (max_rows && ++nr_rows >= max_rows)
451  goto out;
452 
453  if (h->ms.map == NULL && verbose > 1) {
455  MAP__FUNCTION, verbose, fp);
456  fprintf(fp, "%.10s end\n", graph_dotted_line);
457  }
458  }
459 out:
460  free(rem_sq_bracket);
461 
462  return ret;
463 }
464 
465 size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
466 {
467  int i;
468  size_t ret = 0;
469 
470  for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
471  const char *name;
472 
473  if (hists->stats.nr_events[i] == 0)
474  continue;
475 
476  name = perf_event__name(i);
477  if (!strcmp(name, "UNKNOWN"))
478  continue;
479 
480  ret += fprintf(fp, "%16s events: %10d\n", name,
481  hists->stats.nr_events[i]);
482  }
483 
484  return ret;
485 }