1#include <elf.h> 2#include <inttypes.h> 3#include <sys/ttydefaults.h> 4#include <string.h> 5#include "../../util/sort.h" 6#include "../../util/util.h" 7#include "../../util/hist.h" 8#include "../../util/debug.h" 9#include "../../util/symbol.h" 10#include "../browser.h" 11#include "../helpline.h" 12#include "../libslang.h" 13 14/* 2048 lines should be enough for a script output */ 15#define MAX_LINES 2048 16 17/* 160 bytes for one output line */ 18#define AVERAGE_LINE_LEN 160 19 20struct script_line { 21 struct list_head node; 22 char line[AVERAGE_LINE_LEN]; 23}; 24 25struct perf_script_browser { 26 struct ui_browser b; 27 struct list_head entries; 28 const char *script_name; 29 int nr_lines; 30}; 31 32#define SCRIPT_NAMELEN 128 33#define SCRIPT_MAX_NO 64 34/* 35 * Usually the full path for a script is: 36 * /home/username/libexec/perf-core/scripts/python/xxx.py 37 * /home/username/libexec/perf-core/scripts/perl/xxx.pl 38 * So 256 should be long enough to contain the full path. 39 */ 40#define SCRIPT_FULLPATH_LEN 256 41 42/* 43 * When success, will copy the full path of the selected script 44 * into the buffer pointed by script_name, and return 0. 45 * Return -1 on failure. 46 */ 47static int list_scripts(char *script_name) 48{ 49 char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO]; 50 int i, num, choice, ret = -1; 51 52 /* Preset the script name to SCRIPT_NAMELEN */ 53 buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN)); 54 if (!buf) 55 return ret; 56 57 for (i = 0; i < SCRIPT_MAX_NO; i++) { 58 names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN); 59 paths[i] = names[i] + SCRIPT_NAMELEN; 60 } 61 62 num = find_scripts(names, paths); 63 if (num > 0) { 64 choice = ui__popup_menu(num, names); 65 if (choice < num && choice >= 0) { 66 strcpy(script_name, paths[choice]); 67 ret = 0; 68 } 69 } 70 71 free(buf); 72 return ret; 73} 74 75static void script_browser__write(struct ui_browser *browser, 76 void *entry, int row) 77{ 78 struct script_line *sline = list_entry(entry, struct script_line, node); 79 bool current_entry = ui_browser__is_current_entry(browser, row); 80 81 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 82 HE_COLORSET_NORMAL); 83 84 slsmg_write_nstring(sline->line, browser->width); 85} 86 87static int script_browser__run(struct perf_script_browser *browser) 88{ 89 int key; 90 91 if (ui_browser__show(&browser->b, browser->script_name, 92 "Press <- or ESC to exit") < 0) 93 return -1; 94 95 while (1) { 96 key = ui_browser__run(&browser->b, 0); 97 98 /* We can add some special key handling here if needed */ 99 break; 100 } 101 102 ui_browser__hide(&browser->b); 103 return key; 104} 105 106 107int script_browse(const char *script_opt) 108{ 109 char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN]; 110 char *line = NULL; 111 size_t len = 0; 112 ssize_t retlen; 113 int ret = -1, nr_entries = 0; 114 FILE *fp; 115 void *buf; 116 struct script_line *sline; 117 118 struct perf_script_browser script = { 119 .b = { 120 .refresh = ui_browser__list_head_refresh, 121 .seek = ui_browser__list_head_seek, 122 .write = script_browser__write, 123 }, 124 .script_name = script_name, 125 }; 126 127 INIT_LIST_HEAD(&script.entries); 128 129 /* Save each line of the output in one struct script_line object. */ 130 buf = zalloc((sizeof(*sline)) * MAX_LINES); 131 if (!buf) 132 return -1; 133 sline = buf; 134 135 memset(script_name, 0, SCRIPT_FULLPATH_LEN); 136 if (list_scripts(script_name)) 137 goto exit; 138 139 sprintf(cmd, "perf script -s %s ", script_name); 140 141 if (script_opt) 142 strcat(cmd, script_opt); 143 144 if (input_name) { 145 strcat(cmd, " -i "); 146 strcat(cmd, input_name); 147 } 148 149 strcat(cmd, " 2>&1"); 150 151 fp = popen(cmd, "r"); 152 if (!fp) 153 goto exit; 154 155 while ((retlen = getline(&line, &len, fp)) != -1) { 156 strncpy(sline->line, line, AVERAGE_LINE_LEN); 157 158 /* If one output line is very large, just cut it short */ 159 if (retlen >= AVERAGE_LINE_LEN) { 160 sline->line[AVERAGE_LINE_LEN - 1] = '\0'; 161 sline->line[AVERAGE_LINE_LEN - 2] = '\n'; 162 } 163 list_add_tail(&sline->node, &script.entries); 164 165 if (script.b.width < retlen) 166 script.b.width = retlen; 167 168 if (nr_entries++ >= MAX_LINES - 1) 169 break; 170 sline++; 171 } 172 173 if (script.b.width > AVERAGE_LINE_LEN) 174 script.b.width = AVERAGE_LINE_LEN; 175 176 free(line); 177 pclose(fp); 178 179 script.nr_lines = nr_entries; 180 script.b.nr_entries = nr_entries; 181 script.b.entries = &script.entries; 182 183 ret = script_browser__run(&script); 184exit: 185 free(buf); 186 return ret; 187} 188