|
@@ -0,0 +1,189 @@
|
|
|
+#include <elf.h>
|
|
|
+#include <newt.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);
|
|
|
+
|
|
|
+ slsmg_write_nstring(sline->line, browser->width);
|
|
|
+}
|
|
|
+
|
|
|
+static int script_browser__run(struct perf_script_browser *self)
|
|
|
+{
|
|
|
+ int key;
|
|
|
+
|
|
|
+ if (ui_browser__show(&self->b, self->script_name,
|
|
|
+ "Press <- or ESC to exit") < 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ key = ui_browser__run(&self->b, 0);
|
|
|
+
|
|
|
+ /* We can add some special key handling here if needed */
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ ui_browser__hide(&self->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;
|
|
|
+
|
|
|
+ if (line)
|
|
|
+ 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;
|
|
|
+}
|