// 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; 
 | 
} 
 |