// SPDX-License-Identifier: GPL-2.0 
 | 
#include <inttypes.h> 
 | 
#include "perf.h" 
 | 
#include "util/debug.h" 
 | 
#include "util/symbol.h" 
 | 
#include "util/sort.h" 
 | 
#include "util/evsel.h" 
 | 
#include "util/evlist.h" 
 | 
#include "util/machine.h" 
 | 
#include "util/thread.h" 
 | 
#include "tests/hists_common.h" 
 | 
#include <linux/kernel.h> 
 | 
  
 | 
static struct { 
 | 
    u32 pid; 
 | 
    const char *comm; 
 | 
} fake_threads[] = { 
 | 
    { FAKE_PID_PERF1, "perf" }, 
 | 
    { FAKE_PID_PERF2, "perf" }, 
 | 
    { FAKE_PID_BASH,  "bash" }, 
 | 
}; 
 | 
  
 | 
static struct { 
 | 
    u32 pid; 
 | 
    u64 start; 
 | 
    const char *filename; 
 | 
} fake_mmap_info[] = { 
 | 
    { FAKE_PID_PERF1, FAKE_MAP_PERF,   "perf" }, 
 | 
    { FAKE_PID_PERF1, FAKE_MAP_LIBC,   "libc" }, 
 | 
    { FAKE_PID_PERF1, FAKE_MAP_KERNEL, "[kernel]" }, 
 | 
    { FAKE_PID_PERF2, FAKE_MAP_PERF,   "perf" }, 
 | 
    { FAKE_PID_PERF2, FAKE_MAP_LIBC,   "libc" }, 
 | 
    { FAKE_PID_PERF2, FAKE_MAP_KERNEL, "[kernel]" }, 
 | 
    { FAKE_PID_BASH,  FAKE_MAP_BASH,   "bash" }, 
 | 
    { FAKE_PID_BASH,  FAKE_MAP_LIBC,   "libc" }, 
 | 
    { FAKE_PID_BASH,  FAKE_MAP_KERNEL, "[kernel]" }, 
 | 
}; 
 | 
  
 | 
struct fake_sym { 
 | 
    u64 start; 
 | 
    u64 length; 
 | 
    const char *name; 
 | 
}; 
 | 
  
 | 
static struct fake_sym perf_syms[] = { 
 | 
    { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" }, 
 | 
    { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "run_command" }, 
 | 
    { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "cmd_record" }, 
 | 
}; 
 | 
  
 | 
static struct fake_sym bash_syms[] = { 
 | 
    { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" }, 
 | 
    { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "xmalloc" }, 
 | 
    { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "xfree" }, 
 | 
}; 
 | 
  
 | 
static struct fake_sym libc_syms[] = { 
 | 
    { 700, 100, "malloc" }, 
 | 
    { 800, 100, "free" }, 
 | 
    { 900, 100, "realloc" }, 
 | 
    { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "malloc" }, 
 | 
    { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "free" }, 
 | 
    { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "realloc" }, 
 | 
}; 
 | 
  
 | 
static struct fake_sym kernel_syms[] = { 
 | 
    { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "schedule" }, 
 | 
    { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "page_fault" }, 
 | 
    { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "sys_perf_event_open" }, 
 | 
}; 
 | 
  
 | 
static struct { 
 | 
    const char *dso_name; 
 | 
    struct fake_sym *syms; 
 | 
    size_t nr_syms; 
 | 
} fake_symbols[] = { 
 | 
    { "perf", perf_syms, ARRAY_SIZE(perf_syms) }, 
 | 
    { "bash", bash_syms, ARRAY_SIZE(bash_syms) }, 
 | 
    { "libc", libc_syms, ARRAY_SIZE(libc_syms) }, 
 | 
    { "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) }, 
 | 
}; 
 | 
  
 | 
struct machine *setup_fake_machine(struct machines *machines) 
 | 
{ 
 | 
    struct machine *machine = machines__find(machines, HOST_KERNEL_ID); 
 | 
    size_t i; 
 | 
  
 | 
    if (machine == NULL) { 
 | 
        pr_debug("Not enough memory for machine setup\n"); 
 | 
        return NULL; 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { 
 | 
        struct thread *thread; 
 | 
  
 | 
        thread = machine__findnew_thread(machine, fake_threads[i].pid, 
 | 
                         fake_threads[i].pid); 
 | 
        if (thread == NULL) 
 | 
            goto out; 
 | 
  
 | 
        thread__set_comm(thread, fake_threads[i].comm, 0); 
 | 
        thread__put(thread); 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { 
 | 
        struct perf_sample sample = { 
 | 
            .cpumode = PERF_RECORD_MISC_USER, 
 | 
        }; 
 | 
        union perf_event fake_mmap_event = { 
 | 
            .mmap = { 
 | 
                .pid = fake_mmap_info[i].pid, 
 | 
                .tid = fake_mmap_info[i].pid, 
 | 
                .start = fake_mmap_info[i].start, 
 | 
                .len = FAKE_MAP_LENGTH, 
 | 
                .pgoff = 0ULL, 
 | 
            }, 
 | 
        }; 
 | 
  
 | 
        strcpy(fake_mmap_event.mmap.filename, 
 | 
               fake_mmap_info[i].filename); 
 | 
  
 | 
        machine__process_mmap_event(machine, &fake_mmap_event, &sample); 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { 
 | 
        size_t k; 
 | 
        struct dso *dso; 
 | 
  
 | 
        dso = machine__findnew_dso(machine, fake_symbols[i].dso_name); 
 | 
        if (dso == NULL) 
 | 
            goto out; 
 | 
  
 | 
        /* emulate dso__load() */ 
 | 
        dso__set_loaded(dso); 
 | 
  
 | 
        for (k = 0; k < fake_symbols[i].nr_syms; k++) { 
 | 
            struct symbol *sym; 
 | 
            struct fake_sym *fsym = &fake_symbols[i].syms[k]; 
 | 
  
 | 
            sym = symbol__new(fsym->start, fsym->length, 
 | 
                      STB_GLOBAL, STT_FUNC, fsym->name); 
 | 
            if (sym == NULL) { 
 | 
                dso__put(dso); 
 | 
                goto out; 
 | 
            } 
 | 
  
 | 
            symbols__insert(&dso->symbols, sym); 
 | 
        } 
 | 
  
 | 
        dso__put(dso); 
 | 
    } 
 | 
  
 | 
    return machine; 
 | 
  
 | 
out: 
 | 
    pr_debug("Not enough memory for machine setup\n"); 
 | 
    machine__delete_threads(machine); 
 | 
    return NULL; 
 | 
} 
 | 
  
 | 
void print_hists_in(struct hists *hists) 
 | 
{ 
 | 
    int i = 0; 
 | 
    struct rb_root *root; 
 | 
    struct rb_node *node; 
 | 
  
 | 
    if (hists__has(hists, need_collapse)) 
 | 
        root = &hists->entries_collapsed; 
 | 
    else 
 | 
        root = hists->entries_in; 
 | 
  
 | 
    pr_info("----- %s --------\n", __func__); 
 | 
    node = rb_first(root); 
 | 
    while (node) { 
 | 
        struct hist_entry *he; 
 | 
  
 | 
        he = rb_entry(node, struct hist_entry, rb_node_in); 
 | 
  
 | 
        if (!he->filtered) { 
 | 
            pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n", 
 | 
                i, thread__comm_str(he->thread), 
 | 
                he->ms.map->dso->short_name, 
 | 
                he->ms.sym->name, he->stat.period); 
 | 
        } 
 | 
  
 | 
        i++; 
 | 
        node = rb_next(node); 
 | 
    } 
 | 
} 
 | 
  
 | 
void print_hists_out(struct hists *hists) 
 | 
{ 
 | 
    int i = 0; 
 | 
    struct rb_root *root; 
 | 
    struct rb_node *node; 
 | 
  
 | 
    root = &hists->entries; 
 | 
  
 | 
    pr_info("----- %s --------\n", __func__); 
 | 
    node = rb_first(root); 
 | 
    while (node) { 
 | 
        struct hist_entry *he; 
 | 
  
 | 
        he = rb_entry(node, struct hist_entry, rb_node); 
 | 
  
 | 
        if (!he->filtered) { 
 | 
            pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"/%"PRIu64"\n", 
 | 
                i, thread__comm_str(he->thread), he->thread->tid, 
 | 
                he->ms.map->dso->short_name, 
 | 
                he->ms.sym->name, he->stat.period, 
 | 
                he->stat_acc ? he->stat_acc->period : 0); 
 | 
        } 
 | 
  
 | 
        i++; 
 | 
        node = rb_next(node); 
 | 
    } 
 | 
} 
 |