| 1 | |
| 2 | #include <inttypes.h>
|
| 3 | #include <string.h>
|
| 4 | #include <linux/zalloc.h>
|
| 5 | #include <sys/ttydefaults.h>
|
| 6 |
|
| 7 | #include "ui/browser.h"
|
| 8 | #include "ui/helpline.h"
|
| 9 | #include "ui/keysyms.h"
|
| 10 | #include "ui/ui.h"
|
| 11 | #include "util/annotate.h"
|
| 12 | #include "util/annotate-data.h"
|
| 13 | #include "util/evsel.h"
|
| 14 | #include "util/evlist.h"
|
| 15 | #include "util/sort.h"
|
| 16 |
|
| 17 | #define FOLDED_SIGN '+'
|
| 18 | #define UNFOLD_SIGN '-'
|
| 19 | #define NOCHLD_SIGN ' '
|
| 20 |
|
| 21 | struct browser_entry {
|
| 22 | struct list_head node;
|
| 23 | struct annotated_member *data;
|
| 24 | struct type_hist_entry *hists;
|
| 25 | struct browser_entry *parent;
|
| 26 | struct list_head children;
|
| 27 | int indent;
|
| 28 | int nr_entries;
|
| 29 | bool folded;
|
| 30 | };
|
| 31 |
|
| 32 | struct annotated_data_browser {
|
| 33 | struct ui_browser b;
|
| 34 | struct list_head entries;
|
| 35 | struct browser_entry *curr;
|
| 36 | int nr_events;
|
| 37 | };
|
| 38 |
|
| 39 | static struct annotated_data_browser *get_browser(struct ui_browser *uib)
|
| 40 | {
|
| 41 | return container_of(uib, struct annotated_data_browser, b);
|
| 42 | }
|
| 43 |
|
| 44 | static void update_hist_entry(struct type_hist_entry *dst,
|
| 45 | struct type_hist_entry *src)
|
| 46 | {
|
| 47 | dst->nr_samples += src->nr_samples;
|
| 48 | dst->period += src->period;
|
| 49 | }
|
| 50 |
|
| 51 | static int get_member_overhead(struct annotated_data_type *adt,
|
| 52 | struct browser_entry *entry,
|
| 53 | struct evsel *leader)
|
| 54 | {
|
| 55 | struct annotated_member *member = entry->data;
|
| 56 | int i, k;
|
| 57 |
|
| 58 | for (i = 0; i < member->size; i++) {
|
| 59 | struct type_hist *h;
|
| 60 | struct evsel *evsel;
|
| 61 | int offset = member->offset + i;
|
| 62 |
|
| 63 | k = 0;
|
| 64 | for_each_group_evsel(evsel, leader) {
|
| 65 | if (symbol_conf.skip_empty &&
|
| 66 | evsel__hists(evsel)->stats.nr_samples == 0)
|
| 67 | continue;
|
| 68 |
|
| 69 | h = adt->histograms[evsel->core.idx];
|
| 70 | update_hist_entry(&entry->hists[k++], &h->addr[offset]);
|
| 71 | }
|
| 72 | }
|
| 73 | return 0;
|
| 74 | }
|
| 75 |
|
| 76 | static int add_child_entries(struct annotated_data_browser *browser,
|
| 77 | struct browser_entry *parent,
|
| 78 | struct annotated_data_type *adt,
|
| 79 | struct annotated_member *member,
|
| 80 | struct evsel *evsel, int indent)
|
| 81 | {
|
| 82 | struct annotated_member *pos;
|
| 83 | struct browser_entry *entry;
|
| 84 | struct list_head *parent_list;
|
| 85 |
|
| 86 | entry = zalloc(sizeof(*entry));
|
| 87 | if (entry == NULL)
|
| 88 | return -1;
|
| 89 |
|
| 90 | entry->hists = calloc(browser->nr_events, sizeof(*entry->hists));
|
| 91 | if (entry->hists == NULL) {
|
| 92 | free(entry);
|
| 93 | return -1;
|
| 94 | }
|
| 95 |
|
| 96 | entry->data = member;
|
| 97 | entry->parent = parent;
|
| 98 | entry->indent = indent;
|
| 99 | if (get_member_overhead(adt, entry, evsel) < 0) {
|
| 100 | free(entry);
|
| 101 | return -1;
|
| 102 | }
|
| 103 |
|
| 104 | INIT_LIST_HEAD(&entry->children);
|
| 105 | if (parent)
|
| 106 | parent_list = &parent->children;
|
| 107 | else
|
| 108 | parent_list = &browser->entries;
|
| 109 |
|
| 110 | list_add_tail(&entry->node, parent_list);
|
| 111 |
|
| 112 | list_for_each_entry(pos, &member->children, node) {
|
| 113 | int nr = add_child_entries(browser, entry, adt, pos, evsel,
|
| 114 | indent + 1);
|
| 115 | if (nr < 0)
|
| 116 | return nr;
|
| 117 | }
|
| 118 |
|
| 119 |
|
| 120 | if (!list_empty(&member->children)) {
|
| 121 | struct browser_entry *bracket;
|
| 122 |
|
| 123 | bracket = zalloc(sizeof(*bracket));
|
| 124 | if (bracket == NULL)
|
| 125 | return -1;
|
| 126 |
|
| 127 | bracket->indent = indent;
|
| 128 | bracket->parent = entry;
|
| 129 | bracket->folded = true;
|
| 130 | bracket->nr_entries = 1;
|
| 131 |
|
| 132 | INIT_LIST_HEAD(&bracket->children);
|
| 133 | list_add_tail(&bracket->node, &entry->children);
|
| 134 | }
|
| 135 |
|
| 136 |
|
| 137 | entry->folded = true;
|
| 138 | entry->nr_entries = 1;
|
| 139 | return 0;
|
| 140 | }
|
| 141 |
|
| 142 | static u32 count_visible_entries(struct annotated_data_browser *browser)
|
| 143 | {
|
| 144 | int nr = 0;
|
| 145 | struct browser_entry *entry;
|
| 146 |
|
| 147 | list_for_each_entry(entry, &browser->entries, node)
|
| 148 | nr += entry->nr_entries;
|
| 149 |
|
| 150 | return nr;
|
| 151 | }
|
| 152 |
|
| 153 | static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser)
|
| 154 | {
|
| 155 | struct hist_entry *he = browser->b.priv;
|
| 156 | struct annotated_data_type *adt = he->mem_type;
|
| 157 | struct evsel *evsel = hists_to_evsel(he->hists);
|
| 158 |
|
| 159 | INIT_LIST_HEAD(&browser->entries);
|
| 160 |
|
| 161 | add_child_entries(browser, NULL, adt, &adt->self, evsel,
|
| 162 | 0);
|
| 163 |
|
| 164 | browser->b.entries = &browser->entries;
|
| 165 | browser->b.nr_entries = count_visible_entries(browser);
|
| 166 | return 0;
|
| 167 | }
|
| 168 |
|
| 169 | static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser)
|
| 170 | {
|
| 171 | struct browser_entry *pos, *tmp;
|
| 172 |
|
| 173 | list_for_each_entry_safe(pos, tmp, &browser->entries, node) {
|
| 174 | list_del_init(&pos->node);
|
| 175 | zfree(&pos->hists);
|
| 176 | free(pos);
|
| 177 | }
|
| 178 | }
|
| 179 |
|
| 180 | static struct browser_entry *get_first_child(struct browser_entry *entry)
|
| 181 | {
|
| 182 | if (list_empty(&entry->children))
|
| 183 | return NULL;
|
| 184 |
|
| 185 | return list_first_entry(&entry->children, struct browser_entry, node);
|
| 186 | }
|
| 187 |
|
| 188 | static struct browser_entry *get_last_child(struct browser_entry *entry)
|
| 189 | {
|
| 190 | if (list_empty(&entry->children))
|
| 191 | return NULL;
|
| 192 |
|
| 193 | return list_last_entry(&entry->children, struct browser_entry, node);
|
| 194 | }
|
| 195 |
|
| 196 | static bool is_first_child(struct browser_entry *entry)
|
| 197 | {
|
| 198 |
|
| 199 | if (entry->parent == NULL)
|
| 200 | return false;
|
| 201 |
|
| 202 | return get_first_child(entry->parent) == entry;
|
| 203 | }
|
| 204 |
|
| 205 | static bool is_last_child(struct browser_entry *entry)
|
| 206 | {
|
| 207 |
|
| 208 | if (entry->parent == NULL)
|
| 209 | return false;
|
| 210 |
|
| 211 | return get_last_child(entry->parent) == entry;
|
| 212 | }
|
| 213 |
|
| 214 | static struct browser_entry *browser__prev_entry(struct ui_browser *uib,
|
| 215 | struct browser_entry *entry)
|
| 216 | {
|
| 217 | struct annotated_data_browser *browser = get_browser(uib);
|
| 218 | struct browser_entry *first;
|
| 219 |
|
| 220 | first = list_first_entry(&browser->entries, struct browser_entry, node);
|
| 221 |
|
| 222 | while (entry != first) {
|
| 223 | if (is_first_child(entry))
|
| 224 | entry = entry->parent;
|
| 225 | else {
|
| 226 | entry = list_prev_entry(entry, node);
|
| 227 | while (!entry->folded)
|
| 228 | entry = get_last_child(entry);
|
| 229 | }
|
| 230 |
|
| 231 | if (!uib->filter || !uib->filter(uib, &entry->node))
|
| 232 | return entry;
|
| 233 | }
|
| 234 | return first;
|
| 235 | }
|
| 236 |
|
| 237 | static struct browser_entry *browser__next_entry(struct ui_browser *uib,
|
| 238 | struct browser_entry *entry)
|
| 239 | {
|
| 240 | struct annotated_data_browser *browser = get_browser(uib);
|
| 241 | struct browser_entry *last;
|
| 242 |
|
| 243 | last = list_last_entry(&browser->entries, struct browser_entry, node);
|
| 244 | while (!last->folded)
|
| 245 | last = get_last_child(last);
|
| 246 |
|
| 247 | while (entry != last) {
|
| 248 | if (!entry->folded)
|
| 249 | entry = get_first_child(entry);
|
| 250 | else {
|
| 251 | while (is_last_child(entry))
|
| 252 | entry = entry->parent;
|
| 253 |
|
| 254 | entry = list_next_entry(entry, node);
|
| 255 | }
|
| 256 |
|
| 257 | if (!uib->filter || !uib->filter(uib, &entry->node))
|
| 258 | return entry;
|
| 259 | }
|
| 260 | return last;
|
| 261 | }
|
| 262 |
|
| 263 | static void browser__seek(struct ui_browser *uib, off_t offset, int whence)
|
| 264 | {
|
| 265 | struct annotated_data_browser *browser = get_browser(uib);
|
| 266 | struct browser_entry *entry;
|
| 267 |
|
| 268 | if (uib->nr_entries == 0)
|
| 269 | return;
|
| 270 |
|
| 271 | switch (whence) {
|
| 272 | case SEEK_SET:
|
| 273 | entry = list_first_entry(&browser->entries, typeof(*entry), node);
|
| 274 | if (uib->filter && uib->filter(uib, &entry->node))
|
| 275 | entry = browser__next_entry(uib, entry);
|
| 276 | break;
|
| 277 | case SEEK_CUR:
|
| 278 | entry = list_entry(uib->top, typeof(*entry), node);
|
| 279 | break;
|
| 280 | case SEEK_END:
|
| 281 | entry = list_last_entry(&browser->entries, typeof(*entry), node);
|
| 282 | while (!entry->folded)
|
| 283 | entry = get_last_child(entry);
|
| 284 | if (uib->filter && uib->filter(uib, &entry->node))
|
| 285 | entry = browser__prev_entry(uib, entry);
|
| 286 | break;
|
| 287 | default:
|
| 288 | return;
|
| 289 | }
|
| 290 |
|
| 291 | assert(entry != NULL);
|
| 292 |
|
| 293 | if (offset > 0) {
|
| 294 | while (offset-- != 0)
|
| 295 | entry = browser__next_entry(uib, entry);
|
| 296 | } else {
|
| 297 | while (offset++ != 0)
|
| 298 | entry = browser__prev_entry(uib, entry);
|
| 299 | }
|
| 300 |
|
| 301 | uib->top = &entry->node;
|
| 302 | }
|
| 303 |
|
| 304 | static unsigned int browser__refresh(struct ui_browser *uib)
|
| 305 | {
|
| 306 | struct annotated_data_browser *browser = get_browser(uib);
|
| 307 | struct browser_entry *entry, *next;
|
| 308 | int row = 0;
|
| 309 |
|
| 310 | if (uib->top == NULL || uib->top == uib->entries)
|
| 311 | browser__seek(uib, SEEK_SET, 0);
|
| 312 |
|
| 313 | entry = list_entry(uib->top, typeof(*entry), node);
|
| 314 |
|
| 315 | while (true) {
|
| 316 | if (!uib->filter || !uib->filter(uib, &entry->node)) {
|
| 317 | ui_browser__gotorc(uib, row, 0);
|
| 318 | uib->write(uib, entry, row);
|
| 319 | if (uib->top_idx + row == uib->index)
|
| 320 | browser->curr = entry;
|
| 321 | if (++row == uib->rows)
|
| 322 | break;
|
| 323 | }
|
| 324 | next = browser__next_entry(uib, entry);
|
| 325 | if (next == entry)
|
| 326 | break;
|
| 327 |
|
| 328 | entry = next;
|
| 329 | }
|
| 330 |
|
| 331 | return row;
|
| 332 | }
|
| 333 |
|
| 334 | static int browser__show(struct ui_browser *uib)
|
| 335 | {
|
| 336 | struct hist_entry *he = uib->priv;
|
| 337 | struct annotated_data_type *adt = he->mem_type;
|
| 338 | struct annotated_data_browser *browser = get_browser(uib);
|
| 339 | const char *help = "Press 'h' for help on key bindings";
|
| 340 | char title[256];
|
| 341 |
|
| 342 | snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)",
|
| 343 | adt->self.type_name, he->stat.nr_events);
|
| 344 |
|
| 345 | if (ui_browser__show(uib, title, help) < 0)
|
| 346 | return -1;
|
| 347 |
|
| 348 |
|
| 349 | ui_browser__gotorc_title(uib, 0, 0);
|
| 350 | ui_browser__set_color(uib, HE_COLORSET_ROOT);
|
| 351 |
|
| 352 | if (symbol_conf.show_total_period)
|
| 353 | strcpy(title, "Period");
|
| 354 | else if (symbol_conf.show_nr_samples)
|
| 355 | strcpy(title, "Samples");
|
| 356 | else
|
| 357 | strcpy(title, "Percent");
|
| 358 |
|
| 359 | ui_browser__printf(uib, "%*s %10s %10s %10s %s",
|
| 360 | 2 + 11 * (browser->nr_events - 1), "",
|
| 361 | title, "Offset", "Size", "Field");
|
| 362 | ui_browser__write_nstring(uib, "", uib->width);
|
| 363 | return 0;
|
| 364 | }
|
| 365 |
|
| 366 | static void browser__write_overhead(struct ui_browser *uib,
|
| 367 | struct type_hist *total,
|
| 368 | struct type_hist_entry *hist, int row)
|
| 369 | {
|
| 370 | u64 period = hist->period;
|
| 371 | double percent = total->period ? (100.0 * period / total->period) : 0;
|
| 372 | bool current = ui_browser__is_current_entry(uib, row);
|
| 373 | int nr_samples = 0;
|
| 374 |
|
| 375 | ui_browser__set_percent_color(uib, percent, current);
|
| 376 |
|
| 377 | if (symbol_conf.show_total_period)
|
| 378 | ui_browser__printf(uib, " %10" PRIu64, period);
|
| 379 | else if (symbol_conf.show_nr_samples)
|
| 380 | ui_browser__printf(uib, " %10d", nr_samples);
|
| 381 | else
|
| 382 | ui_browser__printf(uib, " %10.2f", percent);
|
| 383 |
|
| 384 | ui_browser__set_percent_color(uib, 0, current);
|
| 385 | }
|
| 386 |
|
| 387 | static void browser__write(struct ui_browser *uib, void *entry, int row)
|
| 388 | {
|
| 389 | struct annotated_data_browser *browser = get_browser(uib);
|
| 390 | struct browser_entry *be = entry;
|
| 391 | struct annotated_member *member = be->data;
|
| 392 | struct hist_entry *he = uib->priv;
|
| 393 | struct annotated_data_type *adt = he->mem_type;
|
| 394 | struct evsel *leader = hists_to_evsel(he->hists);
|
| 395 | struct evsel *evsel;
|
| 396 | int idx = 0;
|
| 397 | bool current = ui_browser__is_current_entry(uib, row);
|
| 398 |
|
| 399 | if (member == NULL) {
|
| 400 |
|
| 401 | ui_browser__set_percent_color(uib, 0, current);
|
| 402 | ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
|
| 403 | ui_browser__write_nstring(uib, "", 11 * browser->nr_events);
|
| 404 | ui_browser__printf(uib, " %10s %10s %*s};",
|
| 405 | "", "", be->indent * 4, "");
|
| 406 | ui_browser__write_nstring(uib, "", uib->width);
|
| 407 | return;
|
| 408 | }
|
| 409 |
|
| 410 | ui_browser__set_percent_color(uib, 0, current);
|
| 411 |
|
| 412 | if (!list_empty(&be->children))
|
| 413 | ui_browser__printf(uib, "%c ", be->folded ? FOLDED_SIGN : UNFOLD_SIGN);
|
| 414 | else
|
| 415 | ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
|
| 416 |
|
| 417 |
|
| 418 | for_each_group_evsel(evsel, leader) {
|
| 419 | struct type_hist *h = adt->histograms[evsel->core.idx];
|
| 420 |
|
| 421 | if (symbol_conf.skip_empty &&
|
| 422 | evsel__hists(evsel)->stats.nr_samples == 0)
|
| 423 | continue;
|
| 424 |
|
| 425 | browser__write_overhead(uib, h, &be->hists[idx++], row);
|
| 426 | }
|
| 427 |
|
| 428 |
|
| 429 | if (be->indent == 0 && !member->var_name) {
|
| 430 | ui_browser__printf(uib, " %#10x %#10x %s%s",
|
| 431 | member->offset, member->size,
|
| 432 | member->type_name,
|
| 433 | list_empty(&member->children) || be->folded? ";" : " {");
|
| 434 | } else {
|
| 435 | ui_browser__printf(uib, " %#10x %#10x %*s%s\t%s%s",
|
| 436 | member->offset, member->size,
|
| 437 | be->indent * 4, "", member->type_name,
|
| 438 | member->var_name ?: "",
|
| 439 | list_empty(&member->children) || be->folded ? ";" : " {");
|
| 440 | }
|
| 441 |
|
| 442 | ui_browser__write_nstring(uib, "", uib->width);
|
| 443 | }
|
| 444 |
|
| 445 | static void annotated_data_browser__fold(struct annotated_data_browser *browser,
|
| 446 | struct browser_entry *entry,
|
| 447 | bool recursive)
|
| 448 | {
|
| 449 | struct browser_entry *child;
|
| 450 |
|
| 451 | if (list_empty(&entry->children))
|
| 452 | return;
|
| 453 | if (entry->folded && !recursive)
|
| 454 | return;
|
| 455 |
|
| 456 | if (recursive) {
|
| 457 | list_for_each_entry(child, &entry->children, node)
|
| 458 | annotated_data_browser__fold(browser, child, true);
|
| 459 | }
|
| 460 |
|
| 461 | entry->nr_entries = 1;
|
| 462 | entry->folded = true;
|
| 463 | }
|
| 464 |
|
| 465 | static void annotated_data_browser__unfold(struct annotated_data_browser *browser,
|
| 466 | struct browser_entry *entry,
|
| 467 | bool recursive)
|
| 468 | {
|
| 469 | struct browser_entry *child;
|
| 470 | int nr_entries;
|
| 471 |
|
| 472 | if (list_empty(&entry->children))
|
| 473 | return;
|
| 474 | if (!entry->folded && !recursive)
|
| 475 | return;
|
| 476 |
|
| 477 | nr_entries = 1;
|
| 478 | list_for_each_entry(child, &entry->children, node) {
|
| 479 | if (recursive)
|
| 480 | annotated_data_browser__unfold(browser, child, true);
|
| 481 |
|
| 482 | nr_entries += child->nr_entries;
|
| 483 | }
|
| 484 |
|
| 485 | entry->nr_entries = nr_entries;
|
| 486 | entry->folded = false;
|
| 487 | }
|
| 488 |
|
| 489 | static void annotated_data_browser__toggle_fold(struct annotated_data_browser *browser,
|
| 490 | bool recursive)
|
| 491 | {
|
| 492 | struct browser_entry *curr = browser->curr;
|
| 493 | struct browser_entry *parent;
|
| 494 |
|
| 495 | parent = curr->parent;
|
| 496 | while (parent) {
|
| 497 | parent->nr_entries -= curr->nr_entries;
|
| 498 | parent = parent->parent;
|
| 499 | }
|
| 500 | browser->b.nr_entries -= curr->nr_entries;
|
| 501 |
|
| 502 | if (curr->folded)
|
| 503 | annotated_data_browser__unfold(browser, curr, recursive);
|
| 504 | else
|
| 505 | annotated_data_browser__fold(browser, curr, recursive);
|
| 506 |
|
| 507 | parent = curr->parent;
|
| 508 | while (parent) {
|
| 509 | parent->nr_entries += curr->nr_entries;
|
| 510 | parent = parent->parent;
|
| 511 | }
|
| 512 | browser->b.nr_entries += curr->nr_entries;
|
| 513 |
|
| 514 | assert(browser->b.nr_entries == count_visible_entries(browser));
|
| 515 | }
|
| 516 |
|
| 517 | static int annotated_data_browser__run(struct annotated_data_browser *browser,
|
| 518 | struct evsel *evsel __maybe_unused,
|
| 519 | struct hist_browser_timer *hbt)
|
| 520 | {
|
| 521 | int delay_secs = hbt ? hbt->refresh : 0;
|
| 522 | int key;
|
| 523 |
|
| 524 | if (browser__show(&browser->b) < 0)
|
| 525 | return -1;
|
| 526 |
|
| 527 | while (1) {
|
| 528 | key = ui_browser__run(&browser->b, delay_secs);
|
| 529 |
|
| 530 | switch (key) {
|
| 531 | case K_TIMER:
|
| 532 | if (hbt)
|
| 533 | hbt->timer(hbt->arg);
|
| 534 | continue;
|
| 535 | case K_F1:
|
| 536 | case 'h':
|
| 537 | ui_browser__help_window(&browser->b,
|
| 538 | "UP/DOWN/PGUP\n"
|
| 539 | "PGDN/SPACE Navigate\n"
|
| 540 | "</> Move to prev/next symbol\n"
|
| 541 | "e Expand/Collapse current entry\n"
|
| 542 | "E Expand/Collapse all children of the current\n"
|
| 543 | "q/ESC/CTRL+C Exit\n\n");
|
| 544 | continue;
|
| 545 | case 'e':
|
| 546 | annotated_data_browser__toggle_fold(browser,
|
| 547 | false);
|
| 548 | break;
|
| 549 | case 'E':
|
| 550 | annotated_data_browser__toggle_fold(browser,
|
| 551 | true);
|
| 552 | break;
|
| 553 | case K_LEFT:
|
| 554 | case '<':
|
| 555 | case '>':
|
| 556 | case K_ESC:
|
| 557 | case 'q':
|
| 558 | case CTRL('c'):
|
| 559 | goto out;
|
| 560 | default:
|
| 561 | ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions");
|
| 562 | continue;
|
| 563 | }
|
| 564 | }
|
| 565 | out:
|
| 566 | ui_browser__hide(&browser->b);
|
| 567 | return key;
|
| 568 | }
|
| 569 |
|
| 570 | int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel,
|
| 571 | struct hist_browser_timer *hbt)
|
| 572 | {
|
| 573 | struct annotated_data_browser browser = {
|
| 574 | .b = {
|
| 575 | .refresh = browser__refresh,
|
| 576 | .seek = browser__seek,
|
| 577 | .write = browser__write,
|
| 578 | .priv = he,
|
| 579 | .extra_title_lines = 1,
|
| 580 | },
|
| 581 | .nr_events = 1,
|
| 582 | };
|
| 583 | int ret;
|
| 584 |
|
| 585 | ui_helpline__push("Press ESC to exit");
|
| 586 |
|
| 587 | if (evsel__is_group_event(evsel)) {
|
| 588 | struct evsel *pos;
|
| 589 | int nr = 0;
|
| 590 |
|
| 591 | for_each_group_evsel(pos, evsel) {
|
| 592 | if (!symbol_conf.skip_empty ||
|
| 593 | evsel__hists(pos)->stats.nr_samples)
|
| 594 | nr++;
|
| 595 | }
|
| 596 | browser.nr_events = nr;
|
| 597 | }
|
| 598 |
|
| 599 | ret = annotated_data_browser__collect_entries(&browser);
|
| 600 | if (ret < 0)
|
| 601 | goto out;
|
| 602 |
|
| 603 |
|
| 604 | browser__refresh(&browser.b);
|
| 605 |
|
| 606 | annotated_data_browser__toggle_fold(&browser, false);
|
| 607 |
|
| 608 | ret = annotated_data_browser__run(&browser, evsel, hbt);
|
| 609 |
|
| 610 | out:
|
| 611 | annotated_data_browser__delete_entries(&browser);
|
| 612 |
|
| 613 | return ret;
|
| 614 | }
|