| // SPDX-License-Identifier: GPL-2.0 | |
| #include <elf.h> | |
| #include <inttypes.h> | |
| #include <sys/ttydefaults.h> | |
| #include <string.h> | |
| #include "../../util/sort.h" | |
| #include "../../util/util.h" | |
| #include "../../util/hist.h" | |
| #include "../../util/debug.h" | |
| #include "../../util/symbol.h" | |
| #include "../browser.h" | |
| #include "../helpline.h" | |
| #include "../libslang.h" | |
| /* 2048 lines should be enough for a script output */ | |
| #define MAX_LINES 2048 | |
| /* 160 bytes for one output line */ | |
| #define AVERAGE_LINE_LEN 160 | |
| struct script_line { | |
| struct list_head node; | |
| char line[AVERAGE_LINE_LEN]; | |
| }; | |
| struct perf_script_browser { | |
| struct ui_browser b; | |
| struct list_head entries; | |
| const char *script_name; | |
| int nr_lines; | |
| }; | |
| #define SCRIPT_NAMELEN 128 | |
| #define SCRIPT_MAX_NO 64 | |
| /* | |
| * Usually the full path for a script is: | |
| * /home/username/libexec/perf-core/scripts/python/xxx.py | |
| * /home/username/libexec/perf-core/scripts/perl/xxx.pl | |
| * So 256 should be long enough to contain the full path. | |
| */ | |
| #define SCRIPT_FULLPATH_LEN 256 | |
| /* | |
| * When success, will copy the full path of the selected script | |
| * into the buffer pointed by script_name, and return 0. | |
| * Return -1 on failure. | |
| */ | |
| static int list_scripts(char *script_name) | |
| { | |
| char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO]; | |
| int i, num, choice, ret = -1; | |
| /* Preset the script name to SCRIPT_NAMELEN */ | |
| buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN)); | |
| if (!buf) | |
| return ret; | |
| for (i = 0; i < SCRIPT_MAX_NO; i++) { | |
| names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN); | |
| paths[i] = names[i] + SCRIPT_NAMELEN; | |
| } | |
| num = find_scripts(names, paths); | |
| if (num > 0) { | |
| choice = ui__popup_menu(num, names); | |
| if (choice < num && choice >= 0) { | |
| strcpy(script_name, paths[choice]); | |
| ret = 0; | |
| } | |
| } | |
| free(buf); | |
| return ret; | |
| } | |
| static void script_browser__write(struct ui_browser *browser, | |
| void *entry, int row) | |
| { | |
| struct script_line *sline = list_entry(entry, struct script_line, node); | |
| bool current_entry = ui_browser__is_current_entry(browser, row); | |
| ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | |
| HE_COLORSET_NORMAL); | |
| ui_browser__write_nstring(browser, sline->line, browser->width); | |
| } | |
| static int script_browser__run(struct perf_script_browser *browser) | |
| { | |
| int key; | |
| if (ui_browser__show(&browser->b, browser->script_name, | |
| "Press ESC to exit") < 0) | |
| return -1; | |
| while (1) { | |
| key = ui_browser__run(&browser->b, 0); | |
| /* We can add some special key handling here if needed */ | |
| break; | |
| } | |
| ui_browser__hide(&browser->b); | |
| return key; | |
| } | |
| int script_browse(const char *script_opt) | |
| { | |
| char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN]; | |
| char *line = NULL; | |
| size_t len = 0; | |
| ssize_t retlen; | |
| int ret = -1, nr_entries = 0; | |
| FILE *fp; | |
| void *buf; | |
| struct script_line *sline; | |
| struct perf_script_browser script = { | |
| .b = { | |
| .refresh = ui_browser__list_head_refresh, | |
| .seek = ui_browser__list_head_seek, | |
| .write = script_browser__write, | |
| }, | |
| .script_name = script_name, | |
| }; | |
| INIT_LIST_HEAD(&script.entries); | |
| /* Save each line of the output in one struct script_line object. */ | |
| buf = zalloc((sizeof(*sline)) * MAX_LINES); | |
| if (!buf) | |
| return -1; | |
| sline = buf; | |
| memset(script_name, 0, SCRIPT_FULLPATH_LEN); | |
| if (list_scripts(script_name)) | |
| goto exit; | |
| sprintf(cmd, "perf script -s %s ", script_name); | |
| if (script_opt) | |
| strcat(cmd, script_opt); | |
| if (input_name) { | |
| strcat(cmd, " -i "); | |
| strcat(cmd, input_name); | |
| } | |
| strcat(cmd, " 2>&1"); | |
| fp = popen(cmd, "r"); | |
| if (!fp) | |
| goto exit; | |
| while ((retlen = getline(&line, &len, fp)) != -1) { | |
| strncpy(sline->line, line, AVERAGE_LINE_LEN); | |
| /* If one output line is very large, just cut it short */ | |
| if (retlen >= AVERAGE_LINE_LEN) { | |
| sline->line[AVERAGE_LINE_LEN - 1] = '\0'; | |
| sline->line[AVERAGE_LINE_LEN - 2] = '\n'; | |
| } | |
| list_add_tail(&sline->node, &script.entries); | |
| if (script.b.width < retlen) | |
| script.b.width = retlen; | |
| if (nr_entries++ >= MAX_LINES - 1) | |
| break; | |
| sline++; | |
| } | |
| if (script.b.width > AVERAGE_LINE_LEN) | |
| script.b.width = AVERAGE_LINE_LEN; | |
| free(line); | |
| pclose(fp); | |
| script.nr_lines = nr_entries; | |
| script.b.nr_entries = nr_entries; | |
| script.b.entries = &script.entries; | |
| ret = script_browser__run(&script); | |
| exit: | |
| free(buf); | |
| return ret; | |
| } |