lkml.org 
[lkml]   [2011]   [Sep]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    Date
    SubjectRe: [PATCH] perf: make perf.data more self-descriptive (v4)
    From
    On Mon, Sep 12, 2011 at 3:45 PM, David Ahern <dsahern@gmail.com> wrote:
    > On 09/07/2011 01:10 PM, Stephane Eranian wrote:
    >>
    >> The goal of this patch is to include more information
    >> about the host environment into the perf.data so it is
    >> more self-descriptive. Overtime, profiles are captured
    >> on various machines and it becomes hard to track what
    >> was recorded, on what machine and when.
    >>
    >> This patch provides a way to solve this by extending
    >> the perf.data file with basic information about the
    >> host machine. To add those extensions, we leverage
    >> the feature bits capabilities of the perf.data format.
    >> The change is backward compatible with existing perf.data
    >> files.
    >>
    >> We define the following useful new extensions:
    >>  - HEADER_HOSTNAME: the hostname
    >>  - HEADER_OSRELEASE: the kernel release number
    >>  - HEADER_ARCH: the hw architecture
    >>  - HEADER_CPUID: the cpu model description
    >>  - HEADER_NRCPUS: number of online/avail cpus
    >>  - HEADER_CMDLINE: perf command line
    >>  - HEADER_VERSION: perf version
    >>  - HEADER_TOPOLOGY: cpu topology
    >>  - HEADER_EVENT_DESC: full event description (attrs)
    >>
    >> The small granularity for the entries is to make it easier
    >> to extend without breaking backward compatiblity. All entries
    >> are provided as ASCII strings, easy to parse, except for the
    >> event description.
    >>
    >> In the second version, we dropped the -I option for the
    >> perf record command. The extra information is systematically
    >> captured. But it's still displayed optionally by perf report.
    >> There are also a couple of cleanups of function prototypes
    >> and global variables.
    >>
    >> In the third version, we:
    >> - fixed missing MIPS support for CPUID
    >> - used u32 instead of int when writing integers to file
    >> - fixed couple of bswap() bugs
    >> - refactorize all write/print functions using function pointers
    >> - improved write_cmdline() to include actual path to perf binary
    >>
    >> In the fourth version, we:
    >> - fixed a couple of int vs. u32 issues
    >> - factorized HEADER_BUILD_ID, HEADER_TRACE_INFO
    >> - added -I option to perf script report
    >> - reverted change to evsel.c (unrelated to meta-data)
    >> - modified write_*() to always write despite errors (adds_feature bit present)
    >> - added HEADER_TOTAL_MEM: total memory installed on this system
    >> - added HEADER_NUMA_TOPOLOGY: shows NUMA nodes and for each memory, cpu list
    >> - renamed HEADER_TOPOLOGY to HEADER_CPU_TOPOLOGY
    >>
    >>
    >> $ perf record -ecycles,instructions noploop 1
    >> noploop for 1 seconds
    >> [ perf record: Woken up 1 times to write data ]
    >> [ perf record: Captured and wrote 0.098 MB perf.data (~4260 samples) ]
    >> $ perf report -I --stdio
    >> #
    >> # captured on: Wed Sep  7 21:00:10 2011
    >> # hostname : quad
    >> # os release : 3.1.0-rc4-tip
    >> # arch : x86_64
    >> # cpuid : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
    >> # nrcpus online : 4
    >> # nrcpus avail : 4
    >> # event = cycles, type = 0, config = 0x0, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 536, 537, 538, 539 }
    >> # event = instructions, type = 0, config = 0x1, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern = 0, id = { 540, 541, 542,
    >> # cmdline : /home/eranian/perfmon/official/tip/build/tools/perf/perf record -ecycles,instructions noploop 1
    >> # perf version : 3.1.0-rc4
    >> # CPU0   sibling cores  : 0-3
    >> # CPU0   sibling threads: 0
    >> # CPU1   sibling cores  : 0-3
    >> # CPU1   sibling threads: 1
    >> # CPU2   sibling cores  : 0-3
    >> # CPU2   sibling threads: 2
    >> # CPU3   sibling cores  : 0-3
    >> # CPU3   sibling threads: 3
    >> # total memory: 8105344 kB
    >> # node0   meminfo : 8320608 kB
    >> # node0   cpu list: 0-3
    >> #
    >> # Events: 995  cycles
    >> #
    >> # Overhead  Command      Shared Object                Symbol
    >> # ........  .......  .................  ....................
    >> #
    >>     99.90%  noploop  noploop            [.] noploop
    >>      0.10%  noploop  [kernel.kallsyms]  [k] trace_hardirqs_on
    >>      0.00%  noploop  [kernel.kallsyms]  [k] lock_release
    >>      0.00%  noploop  [kernel.kallsyms]  [k] intel_pmu_enable_all
    >> ...
    >>
    >>
    >> Signed-off-by: Stephane Eranian <eranian@google.com>
    >> ---
    >>
    >> diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
    >> index 04253c0..191e10e 100644
    >> --- a/tools/perf/Documentation/perf-report.txt
    >> +++ b/tools/perf/Documentation/perf-report.txt
    >> @@ -134,6 +134,11 @@ OPTIONS
    >>       CPUs are specified with -: 0-2. Default is to report samples on all
    >>       CPUs.
    >>
    >> +-I::
    >> +--show-info::
    >> +     Display information about the perf.data file, incl. hostname,
    >> +     os release, perf version, machine desc.
    >> +
    >>  SEE ALSO
    >>  --------
    >>  linkperf:perf-stat[1]
    >> diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
    >> index db01786..ebf8a23 100644
    >> --- a/tools/perf/Documentation/perf-script.txt
    >> +++ b/tools/perf/Documentation/perf-script.txt
    >> @@ -188,6 +188,13 @@ OPTIONS
    >>       CPUs are specified with -: 0-2. Default is to report samples on all
    >>       CPUs.
    >>
    >> +-I::
    >> +--show-info::
    >> +     Display information about the perf.data file, incl. hostname,
    >> +     os release, perf version, machine desc. It can only be used with the
    >> +     perf script report mode and it must be specified after the script name
    >> +     like for instance: perf script report syscall-counts -I.
    >> +
    >>  SEE ALSO
    >>  --------
    >>  linkperf:perf-record[1], linkperf:perf-script-perl[1],
    >> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
    >> index 6b0519f..524ebbf 100644
    >> --- a/tools/perf/builtin-record.c
    >> +++ b/tools/perf/builtin-record.c
    >> @@ -513,6 +513,18 @@ static int __cmd_record(int argc, const char **argv)
    >>       if (have_tracepoints(&evsel_list->entries))
    >>               perf_header__set_feat(&session->header, HEADER_TRACE_INFO);
    >>
    >> +     perf_header__set_feat(&session->header, HEADER_HOSTNAME);
    >> +     perf_header__set_feat(&session->header, HEADER_OSRELEASE);
    >> +     perf_header__set_feat(&session->header, HEADER_ARCH);
    >> +     perf_header__set_feat(&session->header, HEADER_CPUID);
    >> +     perf_header__set_feat(&session->header, HEADER_NRCPUS);
    >> +     perf_header__set_feat(&session->header, HEADER_EVENT_DESC);
    >> +     perf_header__set_feat(&session->header, HEADER_CMDLINE);
    >> +     perf_header__set_feat(&session->header, HEADER_VERSION);
    >> +     perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY);
    >> +     perf_header__set_feat(&session->header, HEADER_TOTAL_MEM);
    >> +     perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY);
    >> +
    >>       /* 512 kiB: default amount of unprivileged mlocked memory */
    >>       if (mmap_pages == UINT_MAX)
    >>               mmap_pages = (512 * 1024) / page_size;
    >> @@ -782,6 +794,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
    >>       int err = -ENOMEM;
    >>       struct perf_evsel *pos;
    >>
    >> +     perf_header__set_cmdline(argc, argv);
    >> +
    >>       evsel_list = perf_evlist__new(NULL, NULL);
    >>       if (evsel_list == NULL)
    >>               return -ENOMEM;
    >> diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
    >> index d7ff277..7721030 100644
    >> --- a/tools/perf/builtin-report.c
    >> +++ b/tools/perf/builtin-report.c
    >> @@ -40,6 +40,7 @@ static char         const *input_name = "perf.data";
    >>  static bool          force, use_tui, use_stdio;
    >>  static bool          hide_unresolved;
    >>  static bool          dont_use_callchains;
    >> +static bool          show_info;
    >>
    >>  static bool          show_threads;
    >>  static struct perf_read_values       show_threads_values;
    >> @@ -276,6 +277,9 @@ static int __cmd_report(void)
    >>                       goto out_delete;
    >>       }
    >>
    >> +     if (show_info)
    >> +             perf_session__fprintf_info(session, stdout);
    >> +
    >>       if (show_threads)
    >>               perf_read_values_init(&show_threads_values);
    >>
    >> @@ -487,6 +491,8 @@ static const struct option options[] = {
    >>       OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
    >>                   "Look for files with symbols relative to this directory"),
    >>       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
    >> +     OPT_BOOLEAN('I', "show-info", &show_info,
    >> +                     "display information about perf.data file"),
    >>       OPT_END()
    >>  };
    >>
    >> diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
    >> index 09024ec..dac86ab 100644
    >> --- a/tools/perf/builtin-script.c
    >> +++ b/tools/perf/builtin-script.c
    >> @@ -22,6 +22,7 @@ static u64                  last_timestamp;
    >>  static u64                   nr_unordered;
    >>  extern const struct option   record_options[];
    >>  static bool                  no_callchain;
    >> +static bool                  show_info = false;
    >>  static const char            *cpu_list;
    >>  static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
    >>
    >> @@ -1083,7 +1084,8 @@ static const struct option options[] = {
    >>                    "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr",
    >>                    parse_output_fields),
    >>       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
    >> -
    >> +     OPT_BOOLEAN('I', "show-info", &show_info,
    >> +                     "display host information from perf.data file"),
    >>       OPT_END()
    >>  };
    >>
    >> @@ -1268,6 +1270,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
    >>                       return -1;
    >>       }
    >>
    >> +     if (show_info)
    >> +             perf_session__fprintf_info(session, stdout);
    >> +
    >>       if (!no_callchain)
    >>               symbol_conf.use_callchain = true;
    >>       else
    >> diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
    >> index 4702e24..b382bd5 100644
    >> --- a/tools/perf/builtin.h
    >> +++ b/tools/perf/builtin.h
    >> @@ -4,7 +4,6 @@
    >>  #include "util/util.h"
    >>  #include "util/strbuf.h"
    >>
    >> -extern const char perf_version_string[];
    >>  extern const char perf_usage_string[];
    >>  extern const char perf_more_info_string[];
    >>
    >> diff --git a/tools/perf/perf.h b/tools/perf/perf.h
    >> index a5fc660..08b0b5e 100644
    >> --- a/tools/perf/perf.h
    >> +++ b/tools/perf/perf.h
    >> @@ -9,18 +9,21 @@ void get_term_dimensions(struct winsize *ws);
    >>  #include "../../arch/x86/include/asm/unistd.h"
    >>  #define rmb()                asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
    >>  #define cpu_relax()  asm volatile("rep; nop" ::: "memory");
    >> +#define CPUINFO_PROC "model name"
    >>  #endif
    >>
    >>  #if defined(__x86_64__)
    >>  #include "../../arch/x86/include/asm/unistd.h"
    >>  #define rmb()                asm volatile("lfence" ::: "memory")
    >>  #define cpu_relax()  asm volatile("rep; nop" ::: "memory");
    >> +#define CPUINFO_PROC "model name"
    >>  #endif
    >>
    >>  #ifdef __powerpc__
    >>  #include "../../arch/powerpc/include/asm/unistd.h"
    >>  #define rmb()                asm volatile ("sync" ::: "memory")
    >>  #define cpu_relax()  asm volatile ("" ::: "memory");
    >> +#define CPUINFO_PROC "cpu"
    >>  #endif
    >>
    >>  #ifdef __s390__
    >> @@ -37,30 +40,35 @@ void get_term_dimensions(struct winsize *ws);
    >>  # define rmb()               asm volatile("" ::: "memory")
    >>  #endif
    >>  #define cpu_relax()  asm volatile("" ::: "memory")
    >> +#define CPUINFO_PROC "cpu type"
    >>  #endif
    >>
    >>  #ifdef __hppa__
    >>  #include "../../arch/parisc/include/asm/unistd.h"
    >>  #define rmb()                asm volatile("" ::: "memory")
    >>  #define cpu_relax()  asm volatile("" ::: "memory");
    >> +#define CPUINFO_PROC "cpu"
    >>  #endif
    >>
    >>  #ifdef __sparc__
    >>  #include "../../arch/sparc/include/asm/unistd.h"
    >>  #define rmb()                asm volatile("":::"memory")
    >>  #define cpu_relax()  asm volatile("":::"memory")
    >> +#define CPUINFO_PROC "cpu"
    >>  #endif
    >>
    >>  #ifdef __alpha__
    >>  #include "../../arch/alpha/include/asm/unistd.h"
    >>  #define rmb()                asm volatile("mb" ::: "memory")
    >>  #define cpu_relax()  asm volatile("" ::: "memory")
    >> +#define CPUINFO_PROC "cpu model"
    >>  #endif
    >>
    >>  #ifdef __ia64__
    >>  #include "../../arch/ia64/include/asm/unistd.h"
    >>  #define rmb()                asm volatile ("mf" ::: "memory")
    >>  #define cpu_relax()  asm volatile ("hint @pause" ::: "memory")
    >> +#define CPUINFO_PROC "model name"
    >>  #endif
    >>
    >>  #ifdef __arm__
    >> @@ -71,6 +79,7 @@ void get_term_dimensions(struct winsize *ws);
    >>   */
    >>  #define rmb()                ((void(*)(void))0xffff0fa0)()
    >>  #define cpu_relax()  asm volatile("":::"memory")
    >> +#define CPUINFO_PROC "Processor"
    >>  #endif
    >>
    >>  #ifdef __mips__
    >> @@ -83,6 +92,7 @@ void get_term_dimensions(struct winsize *ws);
    >>                               : /* no input */                        \
    >>                               : "memory")
    >>  #define cpu_relax()  asm volatile("" ::: "memory")
    >> +#define CPUINFO_PROC "cpu model"
    >>  #endif
    >>
    >>  #include <time.h>
    >> @@ -171,5 +181,6 @@ struct ip_callchain {
    >>  };
    >>
    >>  extern bool perf_host, perf_guest;
    >> +extern const char perf_version_string[];
    >>
    >>  #endif
    >> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
    >> index b6c1ad1..73fa59e 100644
    >> --- a/tools/perf/util/header.c
    >> +++ b/tools/perf/util/header.c
    >> @@ -7,6 +7,7 @@
    >>  #include <stdlib.h>
    >>  #include <linux/list.h>
    >>  #include <linux/kernel.h>
    >> +#include <sys/utsname.h>
    >>
    >>  #include "evlist.h"
    >>  #include "evsel.h"
    >> @@ -17,12 +18,19 @@
    >>  #include "session.h"
    >>  #include "symbol.h"
    >>  #include "debug.h"
    >> +#include "cpumap.h"
    >>
    >>  static bool no_buildid_cache = false;
    >>
    >>  static int event_count;
    >>  static struct perf_trace_event_type *events;
    >>
    >> +static u32 header_argc;
    >> +static const char **header_argv;
    >> +
    >> +static int dsos__write_buildid_table(struct perf_header *header, int fd);
    >> +static int perf_session__cache_build_ids(struct perf_session *session);
    >> +
    >>  int perf_header__push_event(u64 id, const char *name)
    >>  {
    >>       if (strlen(name) > MAX_EVENT_NAME)
    >> @@ -110,6 +118,843 @@ static int write_padded(int fd, const void *bf, size_t count,
    >>       return err;
    >>  }
    >>
    >> +static int do_write_string(int fd, const char *str)
    >> +{
    >> +     u32 len, olen;
    >> +     int ret;
    >> +
    >> +     olen = strlen(str) + 1;
    >> +     len = ALIGN(olen, NAME_ALIGN);
    >> +
    >> +     /* write len, incl. \0 */
    >> +     ret = do_write(fd, &len, sizeof(len));
    >> +     if (ret < 0)
    >> +             return ret;
    >> +
    >> +     return write_padded(fd, str, olen, len);
    >> +}
    >> +
    >> +static char *do_read_string(int fd, struct perf_header *ph)
    >> +{
    >> +     ssize_t sz, ret;
    >> +     u32 len;
    >> +     char *buf;
    >> +
    >> +     sz = read(fd, &len, sizeof(len));
    >> +     if (sz < (ssize_t)sizeof(len))
    >> +             return NULL;
    >> +
    >> +     if (ph->needs_swap)
    >> +             len = bswap_32(len);
    >> +
    >> +     buf = malloc(len);
    >> +     if (!buf)
    >> +             return NULL;
    >> +
    >> +     ret = read(fd, buf, len);
    >> +     if (ret == (ssize_t)len) {
    >> +             /*
    >> +              * strings are padded by zeroes
    >> +              * thus the actual strlen of buf
    >> +              * may be less than len
    >> +              */
    >> +             return buf;
    >> +     }
    >> +
    >> +     free(buf);
    >> +     return NULL;
    >> +}
    >> +
    >> +int
    >> +perf_header__set_cmdline(int argc, const char **argv)
    >> +{
    >> +     int i;
    >> +
    >> +     header_argc = (u32)argc;
    >> +
    >> +     /* do not include NULL termination */
    >> +     header_argv = calloc(argc, sizeof(char *));
    >> +     if (!header_argv)
    >> +             return -ENOMEM;
    >> +
    >> +     /*
    >> +      * must copy argv contents because it gets moved
    >> +      * around during option parsing
    >> +      */
    >> +     for (i = 0; i < argc ; i++)
    >> +             header_argv[i] = argv[i];
    >> +
    >> +     return 0;
    >> +}
    >> +
    >> +static int write_trace_info(int fd, struct perf_header *h __used,
    >> +                         struct perf_evlist *evlist)
    >> +{
    >> +     return read_tracing_data(fd, &evlist->entries);
    >> +}
    >> +
    >> +
    >> +static int write_build_id(int fd, struct perf_header *h,
    >> +                       struct perf_evlist *evlist __used)
    >> +{
    >> +     struct perf_session *session;
    >> +     int err;
    >> +
    >> +     session = container_of(h, struct perf_session, header);
    >> +
    >> +     err = dsos__write_buildid_table(h, fd);
    >> +     if (err < 0) {
    >> +             pr_debug("failed to write buildid table\n");
    >> +             return err;
    >> +     }
    >> +     if (!no_buildid_cache)
    >> +             perf_session__cache_build_ids(session);
    >> +
    >> +     return 0;
    >> +}
    >> +
    >> +static int write_hostname(int fd, struct perf_header *h __used,
    >> +                       struct perf_evlist *evlist __used)
    >> +{
    >> +     struct utsname uts;
    >> +     int ret;
    >> +
    >> +     ret = uname(&uts);
    >> +     if (ret < 0)
    >> +             return -1;
    >> +
    >> +     return do_write_string(fd, uts.nodename);
    >> +}
    >> +
    >> +static int write_osrelease(int fd, struct perf_header *h __used,
    >> +                        struct perf_evlist *evlist __used)
    >> +{
    >> +     struct utsname uts;
    >> +     int ret;
    >> +
    >> +     ret = uname(&uts);
    >> +     if (ret < 0)
    >> +             return -1;
    >> +
    >> +     return do_write_string(fd, uts.release);
    >> +}
    >> +
    >> +static int write_arch(int fd, struct perf_header *h __used,
    >> +                   struct perf_evlist *evlist __used)
    >> +{
    >> +     struct utsname uts;
    >> +     int ret;
    >> +
    >> +     ret = uname(&uts);
    >> +     if (ret < 0)
    >> +             return -1;
    >> +
    >> +     return do_write_string(fd, uts.machine);
    >> +}
    >> +
    >> +static int write_version(int fd, struct perf_header *h __used,
    >> +                      struct perf_evlist *evlist __used)
    >> +{
    >> +     return do_write_string(fd, perf_version_string);
    >> +}
    >> +
    >> +static int write_cpuid(int fd, struct perf_header *h __used,
    >> +                    struct perf_evlist *evlist __used)
    >> +{
    >> +#ifndef CPUINFO_PROC
    >> +#define CPUINFO_PROC NULL
    >> +#endif
    >> +     FILE *file;
    >> +     char buf[256];
    >> +     char *s, *p;
    >> +     const char *search = CPUINFO_PROC;
    >> +
    >> +     file = fopen("/proc/cpuinfo", "r");
    >> +     if (!file)
    >> +             return -1;
    >> +
    >> +     if (search) {
    >> +             while (fgets(buf, sizeof(buf), file)) {
    >> +                     if (strstr(buf, search))
    >> +                             goto found;
    >> +             }
    >> +     }
    >> +     strcpy(buf, "unknown type");
    >> +found:
    >> +     fclose(file);
    >> +
    >> +     s = buf;
    >> +
    >> +     p = strchr(buf, ':');
    >> +     if (p && *(p+1) == ' ' && *(p+2))
    >> +             s = p + 2;
    >> +     p = strchr(s, '\n');
    >> +     if (p)
    >> +             *p = '\0';
    >> +
    >> +     /* squash extra space characters (branding string) */
    >> +     p = s;
    >> +     while (*p) {
    >> +             if (isspace(*p)) {
    >> +                     char *r = p + 1;
    >> +                     char *q = r;
    >> +                     *p = ' ';
    >> +                     while (*q && isspace(*q))
    >> +                             q++;
    >> +                     if (q != (p+1))
    >> +                             while ((*r++ = *q++));
    >> +             }
    >> +             p++;
    >> +     }
    >> +     return do_write_string(fd, s);
    >> +}
    >> +
    >> +static int write_nrcpus(int fd, struct perf_header *h __used,
    >> +                     struct perf_evlist *evlist __used)
    >> +{
    >> +     long nr;
    >> +     u32 nrc, nra;
    >> +     int ret;
    >> +
    >> +     nr = sysconf(_SC_NPROCESSORS_CONF);
    >> +     if (nr < 0)
    >> +             return -1;
    >> +
    >> +     nrc = (u32)(nr & UINT_MAX);
    >> +
    >> +     ret = sysconf(_SC_NPROCESSORS_ONLN);
    >> +     if (ret < 0)
    >> +             return -1;
    >> +
    >> +     nra = (u32)(nr & UINT_MAX);
    >> +
    >> +     ret = do_write(fd, &nrc, sizeof(nrc));
    >> +     if (ret < 0)
    >> +             return ret;
    >> +
    >> +     return do_write(fd, &nra, sizeof(nra));
    >> +}
    >> +
    >> +static int write_event_desc(int fd, struct perf_header *h __used,
    >> +                         struct perf_evlist *evlist)
    >> +{
    >> +     struct perf_evsel *attr;
    >> +     u32 nre = 0, nri, sz;
    >> +     int ret;
    >> +
    >> +     list_for_each_entry(attr, &evlist->entries, node)
    >> +             nre++;
    >> +
    >> +     /*
    >> +      * write number of events
    >> +      */
    >> +     ret = do_write(fd, &nre, sizeof(nre));
    >> +     if (ret < 0)
    >> +             return ret;
    >> +
    >> +     /*
    >> +      * size of perf_event_attr struct
    >> +      */
    >> +     sz = (u32)sizeof(attr->attr);
    >> +     ret = do_write(fd, &sz, sizeof(sz));
    >> +     if (ret < 0)
    >> +             return ret;
    >> +
    >> +     list_for_each_entry(attr, &evlist->entries, node) {
    >> +
    >> +             ret = do_write(fd, &attr->attr, sz);
    >> +             if (ret < 0)
    >> +                     return ret;
    >> +             /*
    >> +              * write number of unique id per event
    >> +              * there is one id per instance of an event
    >> +              *
    >> +              * copy into an nri to be independent of the
    >> +              * type of ids,
    >> +              */
    >> +             nri = attr->ids;
    >> +             ret = do_write(fd, &nri, sizeof(nri));
    >> +             if (ret < 0)
    >> +                     return ret;
    >> +
    >> +             /*
    >> +              * write event string as passed on cmdline
    >> +              */
    >> +             ret = do_write_string(fd, attr->name);
    >> +             if (ret < 0)
    >> +                     return ret;
    >> +             /*
    >> +              * write unique ids for this event
    >> +              */
    >> +             ret = do_write(fd, attr->id, attr->ids * sizeof(u64));
    >> +             if (ret < 0)
    >> +                     return ret;
    >> +     }
    >> +     return 0;
    >> +}
    >> +
    >> +static int write_cmdline(int fd, struct perf_header *h __used,
    >> +                      struct perf_evlist *evlist __used)
    >> +{
    >> +     char buf[MAXPATHLEN];
    >> +     char proc[32];
    >> +     u32 i, n;
    >> +     int ret;
    >> +
    >> +     /*
    >> +      * actual atual path to perf binary
    >> +      */
    >> +     sprintf(proc, "/proc/%d/exe", getpid());
    >> +     ret = readlink(proc, buf, sizeof(buf));
    >> +     if (ret <= 0)
    >> +             return -1;
    >> +
    >> +     /* readlink() does not add null termination */
    >> +     buf[ret] = '\0';
    >> +
    >> +     /* account for binary path */
    >> +     n = header_argc + 1;
    >> +
    >> +     ret = do_write(fd, &n, sizeof(n));
    >> +     if (ret < 0)
    >> +             return ret;
    >> +
    >> +     ret = do_write_string(fd, buf);
    >> +     if (ret < 0)
    >> +             return ret;
    >> +
    >> +     for (i = 0 ; i < header_argc; i++) {
    >> +             ret = do_write_string(fd, header_argv[i]);
    >> +             if (ret < 0)
    >> +                     return ret;
    >> +     }
    >> +     return 0;
    >> +}
    >> +
    >> +static const char *topo_fmt[] = {
    >> +     "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list",
    >> +     "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
    >> +     NULL
    >> +};
    >> +
    >> +static int write_topo_cpu(int fd, int cpu)
    >> +{
    >> +     FILE *fp;
    >> +     char filename[MAXPATHLEN];
    >> +     char buf[MAXPATHLEN], *p, *c;
    >> +     const char **q = topo_fmt;
    >> +     int ret;
    >> +
    >> +     while (*q) {
    >> +
    >> +             sprintf(filename, *q, cpu);
    >> +
    >> +             fp = fopen(filename, "r");
    >> +             if (!fp)
    >> +                     return -1;
    >> +
    >> +             c = fgets(buf, sizeof(buf), fp);
    >> +             if (!c) {
    >> +                     fclose(fp);
    >> +                     return -1;
    >> +             }
    >> +
    >> +             p = strchr(buf, '\n');
    >> +             if (p)
    >> +                     *p = '\0';
    >> +
    >> +             fclose(fp);
    >> +
    >> +             ret = do_write_string(fd, c);
    >> +             if (ret < 0)
    >> +                     return ret;
    >> +             q++;
    >> +     }
    >> +     return 0;
    >> +}
    >> +
    >> +static int write_cpu_topology(int fd, struct perf_header *h __used,
    >> +                       struct perf_evlist *evlist __used)
    >> +{
    >> +     u32 nr, i;
    >> +     long ncpus;
    >> +     int ret;
    >> +
    >> +     ncpus = sysconf(_SC_NPROCESSORS_CONF);
    >> +     if (ncpus < 0)
    >> +             return -1;
    >> +
    >> +     /* hopefully, this will keep compilers happy about the cast */
    >> +     nr = (u32)(ncpus & UINT_MAX);
    >> +
    >> +     ret = do_write(fd, &nr, sizeof(nr));
    >> +     if (ret < 0)
    >> +             return -1;
    >> +
    >> +     for (i = 0; i < nr; i++) {
    >> +             ret = do_write(fd, &i, sizeof(i));
    >> +             if (ret < 0)
    >> +                     return ret;
    >> +
    >> +             ret = write_topo_cpu(fd, i);
    >> +             if (ret < 0)
    >> +                     return ret;
    >> +     }
    >> +     return 0;
    >> +}
    >> +
    >> +static int write_total_mem(int fd, struct perf_header *h __used,
    >> +                       struct perf_evlist *evlist __used)
    >> +{
    >> +     char buf[MAXPATHLEN], *c;
    >> +     FILE *fp;
    >> +     int ret = -1, n;
    >> +     uint64_t mem;
    >> +
    >> +     fp = fopen("/proc/meminfo", "r");
    >> +     if (!fp)
    >> +             return -1;
    >> +
    >> +     while ((c = fgets(buf, sizeof(buf), fp))) {
    >> +             c = strstr(buf, "MemTotal:");
    >> +             if (c)
    >> +                     break;
    >> +     }
    >> +     fclose(fp);
    >> +
    >> +     n = sscanf(buf, "%*s %"PRIu64, &mem);
    >> +     if (n == 1)
    >> +             ret = do_write(fd, &mem, sizeof(mem));
    >> +
    >> +     return ret;
    >> +}
    >> +
    >> +static int write_topo_node(int fd, int node)
    >> +{
    >> +     char filename[MAXPATHLEN];
    >> +     char buf[MAXPATHLEN], *p, *c;
    >> +     FILE *fp;
    >> +     uint64_t mem;
    >> +     int ret;
    >> +
    >> +     sprintf(filename, "/sys/devices/system/node/node%d/meminfo", node);
    >> +     fp = fopen(filename, "r");
    >> +     if (!fp)
    >> +             return -1;
    >> +
    >> +     while ((c = fgets(buf, sizeof(buf), fp))) {
    >> +             c = strstr(buf, "MemTotal:");
    >> +             if (c)
    >> +                     break;
    >> +     }
    >> +     fclose(fp);
    >> +     if (!c)
    >> +             return -1;
    >> +     ret = sscanf(buf, "%*s %*d %*s %"PRIu64, &mem);
    >> +     if (ret != 1)
    >> +             return -1;
    >> +
    >> +     ret = do_write(fd, &mem, sizeof(mem));
    >> +     if (ret < 0)
    >> +             return -1;
    >> +
    >> +     sprintf(filename, "/sys/devices/system/node/node%d/cpulist", node);
    >> +     fp = fopen(filename, "r");
    >> +     if (!fp)
    >> +             return -1;
    >> +
    >> +     c = fgets(buf, sizeof(buf), fp);
    >> +     fclose(fp);
    >> +     if (!c)
    >> +             return -1;
    >> +
    >> +     p = strchr(buf, '\n');
    >> +     if (p)
    >> +             *p = '\0';
    >> +
    >> +     return do_write_string(fd, c);
    >> +}
    >> +
    >> +static int write_numa_topology(int fd, struct perf_header *h __used,
    >> +                       struct perf_evlist *evlist __used)
    >> +{
    >> +     char buf[MAXPATHLEN];
    >> +     FILE *fp;
    >> +     struct cpu_map *node_map;
    >> +     char *c;
    >> +     u32 nr, i, j;
    >> +     int ret;
    >> +
    >> +     fp = fopen("/sys/devices/system/node/online", "r");
    >> +     if (!fp)
    >> +             return -1;
    >> +
    >> +     c = fgets(buf, sizeof(buf), fp);
    >> +     fclose(fp);
    >> +     if (!c)
    >> +             return -1;
    >> +
    >> +     c = strchr(buf, '\n');
    >> +     if (c)
    >> +             *c = '\0';
    >> +
    >> +     node_map = cpu_map__new(buf);
    >> +     if (!node_map)
    >> +             return -1;
    >> +
    >> +     nr = (u32)node_map->nr;
    >> +     ret = do_write(fd, &nr, sizeof(nr));
    >> +     if (ret < 0)
    >> +             return -1;
    >> +
    >> +     for (i = 0; i < nr; i++) {
    >> +             j = (u32)node_map->map[i];
    >> +             ret = do_write(fd, &j, sizeof(j));
    >> +             if (ret < 0)
    >> +                     goto error;
    >> +
    >> +             ret = write_topo_node(fd, i);
    >> +             if (ret < 0)
    >> +                     goto error;
    >> +     }
    >> +     ret = 0;
    >> +error:
    >> +     free(node_map);
    >> +     return ret;
    >> +}
    >> +
    >> +static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
    >> +{
    >> +     char *str = do_read_string(fd, ph);
    >> +     fprintf(fp, "# hostname : %s\n", str);
    >> +     free(str);
    >> +}
    >> +
    >> +static void print_osrelease(struct perf_header *ph, int fd, FILE *fp)
    >> +{
    >> +     char *str = do_read_string(fd, ph);
    >> +     fprintf(fp, "# os release : %s\n", str);
    >> +     free(str);
    >> +}
    >> +
    >> +static void print_arch(struct perf_header *ph, int fd, FILE *fp)
    >> +{
    >> +     char *str = do_read_string(fd, ph);
    >> +     fprintf(fp, "# arch : %s\n", str);
    >> +     free(str);
    >> +}
    >> +
    >> +static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
    >> +{
    >> +     char *str = do_read_string(fd, ph);
    >> +     fprintf(fp, "# cpuid : %s\n", str);
    >> +     free(str);
    >> +}
    >> +
    >> +static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp)
    >> +{
    >> +     ssize_t ret;
    >> +     u32 nr;
    >> +
    >> +     ret = read(fd, &nr, sizeof(nr));
    >> +     if (ret != (ssize_t)sizeof(nr))
    >> +             nr = -1; /* interpreted as error */
    >> +
    >> +     if (ph->needs_swap)
    >> +             nr = bswap_32(nr);
    >> +
    >> +     fprintf(fp, "# nrcpus online : %u\n", nr);
    >> +
    >> +     ret = read(fd, &nr, sizeof(nr));
    >> +     if (ret != (ssize_t)sizeof(nr))
    >> +             nr = -1; /* interpreted as error */
    >> +
    >> +     if (ph->needs_swap)
    >> +             nr = bswap_32(nr);
    >> +
    >> +     fprintf(fp, "# nrcpus avail : %u\n", nr);
    >> +}
    >> +
    >> +static void print_version(struct perf_header *ph, int fd, FILE *fp)
    >> +{
    >> +     char *str = do_read_string(fd, ph);
    >> +     fprintf(fp, "# perf version : %s\n", str);
    >> +     free(str);
    >> +}
    >> +
    >> +static void print_cmdline(struct perf_header *ph, int fd, FILE *fp)
    >> +{
    >> +     ssize_t ret;
    >> +     char *str;
    >> +     u32 nr, i;
    >> +
    >> +     ret = read(fd, &nr, sizeof(nr));
    >> +     if (ret != (ssize_t)sizeof(nr)) {
    >> +             fprintf(fp, "# cmdline : unknown\n");
    >> +             return;
    >> +     }
    >> +
    >> +     if (ph->needs_swap)
    >> +             nr = bswap_32(nr);
    >> +
    >> +     fprintf(fp, "# cmdline : ");
    >> +
    >> +     for (i = 0; i < nr; i++) {
    >> +             str = do_read_string(fd, ph);
    >> +             fprintf(fp, "%s ", str);
    >> +             free(str);
    >> +     }
    >> +     fputc('\n', fp);
    >> +}
    >> +
    >> +static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp)
    >> +{
    >> +     ssize_t ret;
    >> +     u32 nr, c, i;
    >> +     char *str;
    >> +
    >> +     ret = read(fd, &nr, sizeof(nr));
    >> +     if (ret != (ssize_t)sizeof(nr)) {
    >> +             fprintf(fp, "# cpu topology  : not available\n");
    >> +             return;
    >> +     }
    >> +
    >> +     if (ph->needs_swap)
    >> +             nr = bswap_32(nr);
    >> +
    >> +     for (i = 0; i < nr; i++) {
    >> +
    >> +             ret = read(fd, &c, sizeof(c));
    >> +             if (ret != (ssize_t)sizeof(c))
    >> +                     c = -1; /* interpreted as error */
    >> +
    >> +             if (ph->needs_swap)
    >> +                     c = bswap_32(c);
    >> +
    >> +             str = do_read_string(fd, ph);
    >> +             fprintf(fp, "# CPU%u sibling cores  : %s\n", c, str);
    >> +             free(str);
    >> +
    >> +             str = do_read_string(fd, ph);
    >> +             fprintf(fp, "# CPU%u sibling threads: %s\n", c, str);
    >> +             free(str);
    >> +     }
    >> +}
    >> +
    >> +static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
    >> +{
    >> +     struct perf_event_attr attr;
    >> +     uint64_t id;
    >> +     void *buf = NULL;
    >> +     char *str;
    >> +     u32 nre, sz, nr, i, j, msz;
    >> +     int ret;
    >> +
    >> +     /* number of events */
    >> +     ret = read(fd, &nre, sizeof(nre));
    >> +     if (ret != (ssize_t)sizeof(nre))
    >> +             goto error;
    >> +
    >> +     if (ph->needs_swap)
    >> +             nre = bswap_32(nre);
    >> +
    >> +     ret = read(fd, &sz, sizeof(sz));
    >> +     if (ret != (ssize_t)sizeof(sz))
    >> +             goto error;
    >> +
    >> +     if (ph->needs_swap)
    >> +             sz = bswap_32(sz);
    >> +
    >> +     /*
    >> +      * ensure it is at least to our ABI rev
    >> +      */
    >> +     if (sz < (u32)sizeof(attr))
    >> +             goto error;
    >> +
    >> +     memset(&attr, 0, sizeof(attr));
    >> +
    >> +     /* read entire region to sync up to next field */
    >> +     buf = malloc(sz);
    >> +     if (!buf)
    >> +             goto error;
    >> +
    >> +     msz = sizeof(attr);
    >> +     if (sz < msz)
    >> +             msz = sz;
    >> +
    >> +     for (i = 0 ; i < nre; i++) {
    >> +
    >> +             ret = read(fd, buf, sz);
    >> +             if (ret != (ssize_t)sz)
    >> +                     goto error;
    >> +
    >> +             if (ph->needs_swap)
    >> +                     perf_event__attr_swap(buf);
    >> +
    >> +             memcpy(&attr, buf, msz);
    >> +
    >> +             ret = read(fd, &nr, sizeof(nr));
    >> +             if (ret != (ssize_t)sizeof(nr))
    >> +                     goto error;
    >> +
    >> +             if (ph->needs_swap)
    >> +                     nr = bswap_32(nr);
    >> +
    >> +             str = do_read_string(fd, ph);
    >> +             fprintf(fp, "# event = %s, ", str);
    >> +             free(str);
    >> +
    >> +             fprintf(fp, "type = %d, config = 0x%"PRIx64
    >> +                         ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64,
    >> +                             attr.type,
    >> +                             (u64)attr.config,
    >> +                             (u64)attr.config1,
    >> +                             (u64)attr.config2);
    >> +
    >> +             fprintf(fp, ", excl_usr = %d, excl_kern = %d",
    >> +                             attr.exclude_user,
    >> +                             attr.exclude_kernel);
    >> +
    >> +             if (nr)
    >> +                     fprintf(fp, ", id = {");
    >> +
    >> +             for (j = 0 ; j < nr; j++) {
    >> +                     ret = read(fd, &id, sizeof(id));
    >> +                     if (ret != (ssize_t)sizeof(id))
    >> +                             goto error;
    >> +
    >> +                     if (ph->needs_swap)
    >> +                             id = bswap_64(id);
    >> +
    >> +                     if (j)
    >> +                             fputc(',', fp);
    >> +
    >> +                     fprintf(fp, " %"PRIu64, id);
    >> +             }
    >> +             if (nr && j == nr)
    >> +                     fprintf(fp, " }");
    >> +             fputc('\n', fp);
    >> +     }
    >> +     free(buf);
    >> +     return;
    >> +error:
    >> +     fprintf(fp, "# event desc: not available or unable to read\n");
    >> +}
    >> +
    >> +static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp)
    >> +{
    >> +     uint64_t mem;
    >> +     ssize_t ret;
    >> +
    >> +     ret = read(fd, &mem, sizeof(mem));
    >> +     if (ret != sizeof(mem))
    >> +             goto error;
    >> +
    >> +     if (h->needs_swap)
    >> +             mem = bswap_64(mem);
    >> +
    >> +     fprintf(fp, "# total memory: %"PRIu64" kB\n", mem);
    >> +     return;
    >> +error:
    >> +     fprintf(fp, "# total memory: unknown\n");
    >> +}
    >> +
    >> +static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
    >> +{
    >> +     ssize_t ret;
    >> +     u32 nr, c, i;
    >> +     char *str;
    >> +     uint64_t mem;
    >> +
    >> +     /* nr nodes */
    >> +     ret = read(fd, &nr, sizeof(nr));
    >> +     if (ret != (ssize_t)sizeof(nr))
    >> +             goto error;
    >> +
    >> +     if (h->needs_swap)
    >> +             nr = bswap_32(nr);
    >> +
    >> +     for (i = 0; i < nr; i++) {
    >> +
    >> +             /* node number */
    >> +             ret = read(fd, &c, sizeof(c));
    >> +             if (ret != (ssize_t)sizeof(c))
    >> +                     goto error;
    >> +
    >> +             if (h->needs_swap)
    >> +                     c = bswap_32(c);
    >> +
    >> +             ret = read(fd, &mem, sizeof(mem));
    >> +             if (ret != sizeof(mem))
    >> +                     goto error;
    >> +
    >> +             if (h->needs_swap)
    >> +                     mem = bswap_64(mem);
    >> +
    >> +             fprintf(fp, "# node%u meminfo : %"PRIu64" kB\n", c, mem);
    >> +
    >> +             str = do_read_string(fd, h);
    >> +             fprintf(fp, "# node%u cpu list: %s\n", c, str);
    >> +             free(str);
    >> +     }
    >> +     return;
    >> +error:
    >> +     fprintf(fp, "# numa topology : not available\n");
    >> +}
    >> +
    >> +struct feature_ops {
    >> +     int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
    >> +     void (*print)(struct perf_header *h, int fd, FILE *fp);
    >> +};
    >> +
    >> +#define FEAT_OP(n, w, p) [n] = { .write = w, .print = p }
    >> +
    >> +static struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
    >> +     FEAT_OP(HEADER_TRACE_INFO, write_trace_info, NULL),
    >> +     FEAT_OP(HEADER_BUILD_ID, write_build_id, NULL),
    >> +     FEAT_OP(HEADER_HOSTNAME, write_hostname, print_hostname),
    >> +     FEAT_OP(HEADER_OSRELEASE, write_osrelease, print_osrelease),
    >> +     FEAT_OP(HEADER_ARCH, write_arch, print_arch),
    >> +     FEAT_OP(HEADER_CPUID, write_cpuid, print_cpuid),
    >> +     FEAT_OP(HEADER_NRCPUS, write_nrcpus, print_nrcpus),
    >> +     FEAT_OP(HEADER_EVENT_DESC, write_event_desc, print_event_desc),
    >> +     FEAT_OP(HEADER_CMDLINE, write_cmdline, print_cmdline),
    >> +     FEAT_OP(HEADER_VERSION, write_version, print_version),
    >> +     FEAT_OP(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology),
    >> +     FEAT_OP(HEADER_TOTAL_MEM, write_total_mem, print_total_mem),
    >> +     FEAT_OP(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology),
    >> +};
    >> +
    >> +static int perf_header_fprintf_info(struct perf_file_section *section,
    >> +                                 struct perf_header *ph,
    >> +                                 int feat, int fd, void *data)
    >> +{
    >> +     FILE *fp = data;
    >> +
    >> +     if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
    >> +             pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
    >> +                             "%d, continuing...\n", section->offset, feat);
    >> +             return 0;
    >> +     }
    >> +     if (feat < HEADER_TRACE_INFO || feat >= HEADER_LAST_FEATURE) {
    >> +             pr_warning("unknown feature %d\n", feat);
    >> +             return -1;
    >> +     }
    >> +
    >> +     if (feat_ops[feat].print)
    >> +             feat_ops[feat].print(ph, fd, fp);
    >> +
    >> +     return 0;
    >> +}
    >> +
    >> +int perf_header__fprintf_info(struct perf_session *session, FILE *fp __used)
    >> +{
    >> +     int fd = session->fd;
    >> +     struct perf_header *header = &session->header;
    >> +     perf_header__process_sections(header, fd, fp, perf_header_fprintf_info);
    >> +     return 0;
    >> +}
    >> +
    >>  #define dsos__for_each_with_build_id(pos, head)      \
    >>       list_for_each_entry(pos, head, node)    \
    >>               if (!pos->has_build_id)         \
    >> @@ -356,15 +1201,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
    >>       return ret;
    >>  }
    >>
    >> +static int do_write_feat(int fd, struct perf_header *h, int type,
    >> +                      struct perf_file_section **p,
    >> +                      struct perf_evlist *evlist)
    >> +{
    >> +     int err;
    >> +     int ret = 0;
    >> +
    >> +     if (perf_header__has_feat(h, type)) {
    >> +
    >> +             (*p)->offset = lseek(fd, 0, SEEK_CUR);
    >> +
    >> +             err = feat_ops[type].write(fd, h, evlist);
    >> +             if (err < 0) {
    >> +                     pr_debug("failed to write feature %d\n", type);
    >> +
    >> +                     /* undo anything written */
    >> +                     lseek(fd, (*p)->offset, SEEK_SET);
    >> +
    >> +                     return -1;
    >> +             }
    >> +             (*p)->size = lseek(fd, 0, SEEK_CUR) - (*p)->offset;
    >> +             (*p)++;
    >> +     }
    >> +     return ret;
    >> +}
    >> +
    >>  static int perf_header__adds_write(struct perf_header *header,
    >>                                  struct perf_evlist *evlist, int fd)
    >>  {
    >>       int nr_sections;
    >>       struct perf_session *session;
    >> -     struct perf_file_section *feat_sec;
    >> +     struct perf_file_section *feat_sec, *p;
    >>       int sec_size;
    >>       u64 sec_start;
    >> -     int idx = 0, err;
    >> +     int err;
    >>
    >>       session = container_of(header, struct perf_session, header);
    >>
    >> @@ -376,7 +1247,7 @@ static int perf_header__adds_write(struct perf_header *header,
    >>       if (!nr_sections)
    >>               return 0;
    >>
    >> -     feat_sec = calloc(sizeof(*feat_sec), nr_sections);
    >> +     feat_sec = p = calloc(sizeof(*feat_sec), nr_sections);
    >>       if (feat_sec == NULL)
    >>               return -ENOMEM;
    >>
    >> @@ -385,36 +1256,65 @@ static int perf_header__adds_write(struct perf_header *header,
    >>       sec_start = header->data_offset + header->data_size;
    >>       lseek(fd, sec_start + sec_size, SEEK_SET);
    >>
    >> -     if (perf_header__has_feat(header, HEADER_TRACE_INFO)) {
    >> -             struct perf_file_section *trace_sec;
    >> -
    >> -             trace_sec = &feat_sec[idx++];
    >> +     err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist);
    >> +     if (err)
    >> +             goto out_free;
    >>
    >> -             /* Write trace info */
    >> -             trace_sec->offset = lseek(fd, 0, SEEK_CUR);
    >> -             read_tracing_data(fd, &evlist->entries);
    >> -             trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
    >> +     err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist);
    >> +     if (err) {
    >> +             perf_header__clear_feat(header, HEADER_BUILD_ID);
    >> +             goto out_free;
    >>       }
    >>
    >> -     if (perf_header__has_feat(header, HEADER_BUILD_ID)) {
    >> -             struct perf_file_section *buildid_sec;
    >> +     err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_HOSTNAME);
    >>
    >> -             buildid_sec = &feat_sec[idx++];
    >> +     err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_OSRELEASE);
    >>
    >> -             /* Write build-ids */
    >> -             buildid_sec->offset = lseek(fd, 0, SEEK_CUR);
    >> -             err = dsos__write_buildid_table(header, fd);
    >> -             if (err < 0) {
    >> -                     pr_debug("failed to write buildid table\n");
    >> -                     goto out_free;
    >> -             }
    >> -             buildid_sec->size = lseek(fd, 0, SEEK_CUR) -
    >> -                                       buildid_sec->offset;
    >> -             if (!no_buildid_cache)
    >> -                     perf_session__cache_build_ids(session);
    >> -     }
    >> +     err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_ARCH);
    >> +
    >> +     err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_CPUID);
    >> +
    >> +     err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_NRCPUS);
    >> +
    >> +     err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_EVENT_DESC);
    >> +
    >> +     err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_CMDLINE);
    >> +
    >> +     err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_VERSION);
    >> +
    >> +     err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY);
    >> +
    >> +     err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_TOTAL_MEM);
    >> +
    >> +     err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist);
    >> +     if (err)
    >> +             perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY);
    >>
    >>       lseek(fd, sec_start, SEEK_SET);
    >> +     /*
    >> +      * may write more than needed due to dropped feature, but
    >> +      * this is okay, reader will skip the mising entries
    >> +      */
    >>       err = do_write(fd, feat_sec, sec_size);
    >>       if (err < 0)
    >>               pr_debug("failed to write feature section\n");
    >> @@ -554,9 +1454,10 @@ static int perf_header__getbuffer64(struct perf_header *header,
    >>  }
    >>
    >>  int perf_header__process_sections(struct perf_header *header, int fd,
    >> +                               void *data,
    >>                                 int (*process)(struct perf_file_section *section,
    >> -                                              struct perf_header *ph,
    >> -                                              int feat, int fd))
    >> +                               struct perf_header *ph,
    >> +                               int feat, int fd, void *data))
    >>  {
    >>       struct perf_file_section *feat_sec;
    >>       int nr_sections;
    >> @@ -584,7 +1485,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
    >>               if (perf_header__has_feat(header, feat)) {
    >>                       struct perf_file_section *sec = &feat_sec[idx++];
    >>
    >> -                     err = process(sec, header, feat, fd);
    >> +                     err = process(sec, header, feat, fd, data);
    >>                       if (err < 0)
    >>                               break;
    >>               }
    >> @@ -796,7 +1697,7 @@ out:
    >>
    >>  static int perf_file_section__process(struct perf_file_section *section,
    >>                                     struct perf_header *ph,
    >> -                                   int feat, int fd)
    >> +                                   int feat, int fd, void *data __used)
    >>  {
    >>       if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
    >>               pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
    >> @@ -935,7 +1836,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
    >>               event_count =  f_header.event_types.size / sizeof(struct perf_trace_event_type);
    >>       }
    >>
    >> -     perf_header__process_sections(header, fd, perf_file_section__process);
    >> +     perf_header__process_sections(header, fd, NULL,
    >> +                                   perf_file_section__process);
    >>
    >>       lseek(fd, header->data_offset, SEEK_SET);
    >>
    >> diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
    >> index 1886256..455480b 100644
    >> --- a/tools/perf/util/header.h
    >> +++ b/tools/perf/util/header.h
    >> @@ -12,6 +12,19 @@
    >>  enum {
    >>       HEADER_TRACE_INFO = 1,
    >>       HEADER_BUILD_ID,
    >> +
    >> +     HEADER_HOSTNAME,
    >> +     HEADER_OSRELEASE,
    >> +     HEADER_ARCH,
    >> +     HEADER_CPUID,
    >> +     HEADER_NRCPUS,
    >> +     HEADER_EVENT_DESC,
    >> +     HEADER_CMDLINE,
    >> +     HEADER_VERSION,
    >> +     HEADER_CPU_TOPOLOGY,
    >> +     HEADER_TOTAL_MEM,
    >> +     HEADER_NUMA_TOPOLOGY,
    >> +
    >>       HEADER_LAST_FEATURE,
    >>  };
    >>
    >> @@ -68,10 +81,15 @@ void perf_header__set_feat(struct perf_header *header, int feat);
    >>  void perf_header__clear_feat(struct perf_header *header, int feat);
    >>  bool perf_header__has_feat(const struct perf_header *header, int feat);
    >>
    >> +int perf_header__set_cmdline(int argc, const char **argv);
    >> +
    >>  int perf_header__process_sections(struct perf_header *header, int fd,
    >> +                               void *data,
    >>                                 int (*process)(struct perf_file_section *section,
    >> -                                              struct perf_header *ph,
    >> -                                              int feat, int fd));
    >> +                               struct perf_header *ph,
    >> +                               int feat, int fd, void *data));
    >> +
    >> +int perf_header__fprintf_info(struct perf_session *session, FILE *fp);
    >>
    >>  int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
    >>                         const char *name, bool is_kallsyms);
    >> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
    >> index 72458d9..54613a2 100644
    >> --- a/tools/perf/util/session.c
    >> +++ b/tools/perf/util/session.c
    >> @@ -1326,3 +1326,21 @@ int perf_session__cpu_bitmap(struct perf_session *session,
    >>
    >>       return 0;
    >>  }
    >> +
    >> +void perf_session__fprintf_info(struct perf_session *session, FILE *fp)
    >> +{
    >> +     struct stat st;
    >> +     int ret;
    >> +
    >> +     if (session == NULL || fp == NULL)
    >> +             return;
    >> +
    >> +     ret = fstat(session->fd, &st);
    >> +     if (ret == -1)
    >> +             return;
    >> +
    >> +     fprintf(fp, "#\n# captured on: %s", ctime(&st.st_ctime));
    >> +     ret = perf_header__fprintf_info(session, fp);
    >> +     if (ret == 0)
    >> +             fprintf(fp, "#\n");
    >> +}
    >> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
    >> index 170601e..33a3a46 100644
    >> --- a/tools/perf/util/session.h
    >> +++ b/tools/perf/util/session.h
    >> @@ -176,4 +176,5 @@ void perf_session__print_ip(union perf_event *event,
    >>  int perf_session__cpu_bitmap(struct perf_session *session,
    >>                            const char *cpu_list, unsigned long *cpu_bitmap);
    >>
    >> +void perf_session__fprintf_info(struct perf_session *session, FILE *fp);
    >>  #endif /* __PERF_SESSION_H */
    >
    > Reviewed-by: David Ahern <dsahern@gmail.com>
    > Tested-by: David Ahern <dsahern@gmail.com>
    >
    > The problem with endianness handling I mentioned last week is related to
    > the assumption that the HEADER_BUILD_ID is set. In my test case it is
    > not (-B flag on perf-record) and that causes the aborts. A problem for
    > another day which I can address it when I submit the patch to handle
    > swapping for the feature mask.
    >
    Ok, now I understand your point better.

    The thing is that the endianess detection done by perf is currently broken.
    It assumes that if the size of perf_event_attr saved in the file (attr_size)
    does not match sizeof(perf_event_attr) then it must be because of endianess
    issues. The reality is that it they can be different because the the ABI
    has been extended (and I am sure it will), and this a perf tool may
    have a larger struct perf_event_attr, than an old perf.data file, yet it
    should be able to read it. We need to fix this issue now and not once
    we hit the problem with extended ABI. I have a couple of ideas on this
    and they revolve around using the magic prefix as the indicator of endianess
    (and also detect older perf.data files).
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2011-09-12 15:57    [W:6.184 / U:0.272 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site