Re: [PATCH] perf: make perf.data more self-descriptive (v5)

From: Stephane Eranian
Date: Thu Sep 22 2011 - 11:40:21 EST


On Thu, Sep 22, 2011 at 5:35 PM, David Ahern <dsahern@xxxxxxxxx> wrote:
> On 09/22/2011 06:31 AM, 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_CPUDESC: generic CPU 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)
>> Â- HEADER_CPUID: easy-to-parse low level CPU identication
>>
>> 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
>>
>> In the fifth version we:
>> - renamed HEADER_CPUID -> HEADER_CPUDESC. It provides a description of the CPU
>> - added HEADER_CPUID to capture the CPU identification
>>
>> The advantage of CPUID over CPUDESC is that it is easy to parse automatically.
>> The CPUDESC provides a human-readable description of the CPU model. On X86, for
>> instance, CPUDESC captures the "branding" string, whereas CPUID captures vendor,
>> family, model, stepping. We also provide the PPC implementation, where CPUID
>> captures the CPU version, revision. ÂThe CPUID feature is implemented via the
>> get_cpuid() arch callback. ÂWhen the arch does not provide the callback, the
>> CPUID is simply not captured.
>>
>> $ perf report -I --stdio
>> #
>> # captured on: Thu Sep 22 14:16:57 2011
>> # hostname : quad
>> # os release : 3.1.0-rc4-tip
>> # arch : x86_64
>> # cpudesc : 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 = { 9, 10, 11,
>> # cmdline : /usr/bin/perf record date
>> # 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: 8093212 kB
>> # cpuid : GenuineIntel,6,15,11
>> #
>> # Events: 6 Âcycles
>> #
>> # Overhead ÂCommand   ÂShared Object        ÂSymbol
>> # ........ Â....... Â................. Â....................
>> #
>> Â Â 98.56% Â Â date Â[kernel.kallsyms] Â[k] clear_page_c
>> Â Â Â1.42% Â Â date Â[kernel.kallsyms] Â[k] lock_release
>> Â Â Â0.02% Â Â date Â[kernel.kallsyms] Â[k] intel_pmu_enable_all
>> ...
>>
>>
>> Signed-off-by: Stephane Eranian <eranian@xxxxxxxxxx>
>> ---
>>
>> 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/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile
>> index 15130b5..744e629 100644
>> --- a/tools/perf/arch/powerpc/Makefile
>> +++ b/tools/perf/arch/powerpc/Makefile
>> @@ -2,3 +2,4 @@ ifndef NO_DWARF
>> ÂPERF_HAVE_DWARF_REGS := 1
>> ÂLIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
>> Âendif
>> +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
>> diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c
>> new file mode 100644
>> index 0000000..d4bf958
>> --- /dev/null
>> +++ b/tools/perf/arch/powerpc/util/header.c
>> @@ -0,0 +1,36 @@
>> +#include <sys/types.h>
>> +#include <unistd.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +#include "../../util/header.h"
>> +
>> +static inline unsigned long mfspr(int rn)
>> +{
>> + Â Â unsigned long rval;
>> + Â Â asm volatile("mfspr %0," __stringify(rn) : "=r" (rval));
>> + Â Â return Ârval;
>> +}
>
> Fails to compile on powerpc:
>
> arch/powerpc/util/header.c: In function âmfsprâ:
> arch/powerpc/util/header.c:12: error: expected â:â or â)â before
> â__stringifyâ
> cc1: warnings being treated as errors
> arch/powerpc/util/header.c:9: error: unused parameter ârnâ
> make: *** [/tmp/stephane/arch/powerpc/util/header.o] Error 1
> make: *** Waiting for unfinished jobs....
>

Argh, I don't have a PPC system, so I made this up (based on libpfm4 code).

Does adding:

#define __stringify_1(x) #x
#define __stringify(x) __stringify_1(x)

Solve the problem?
> David
>
>
>> +
>> +#define SPRN_PVR Â Â Â Â0x11F Â Â Â Â/* Processor Version Register */
>> +#define PVR_VER(pvr) Â Â(((pvr) >> Â16) & 0xFFFF) /* Version field */
>> +#define PVR_REV(pvr) Â Â(((pvr) >> Â 0) & 0xFFFF) /* Revison field */
>> +
>> +int
>> +get_cpuid(char *buffer, size_t sz)
>> +{
>> + Â Â unsigned long pvr;
>> + Â Â int nb;
>> +
>> + Â Â pvr = mfspr(SPRN_PVR);
>> +
>> + Â Â nb = snprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr));
>> +
>> + Â Â /* look for end marker to ensure the entire data fit */
>> + Â Â if (strchr(buffer, '$')) {
>> + Â Â Â Â Â Â buffer[nb-1] = '\0';
>> + Â Â Â Â Â Â return 0;
>> + Â Â }
>> + Â Â return -1;
>> +}
>> diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile
>> index 15130b5..744e629 100644
>> --- a/tools/perf/arch/x86/Makefile
>> +++ b/tools/perf/arch/x86/Makefile
>> @@ -2,3 +2,4 @@ ifndef NO_DWARF
>> ÂPERF_HAVE_DWARF_REGS := 1
>> ÂLIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
>> Âendif
>> +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
>> diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c
>> new file mode 100644
>> index 0000000..f940060
>> --- /dev/null
>> +++ b/tools/perf/arch/x86/util/header.c
>> @@ -0,0 +1,59 @@
>> +#include <sys/types.h>
>> +#include <unistd.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +#include "../../util/header.h"
>> +
>> +static inline void
>> +cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c,
>> + Â Â Âunsigned int *d)
>> +{
>> + Â Â __asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t"
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â "movl %%ebx, %%esi\n\t.byte 0x5b"
>> + Â Â Â Â Â Â Â Â Â Â : "=a" (*a),
>> + Â Â Â Â Â Â Â Â Â Â "=S" (*b),
>> + Â Â Â Â Â Â Â Â Â Â "=c" (*c),
>> + Â Â Â Â Â Â Â Â Â Â "=d" (*d)
>> + Â Â Â Â Â Â Â Â Â Â : "a" (op));
>> +}
>> +
>> +int
>> +get_cpuid(char *buffer, size_t sz)
>> +{
>> + Â Â unsigned int a, b, c, d, lvl;
>> + Â Â int family = -1, model = -1, step = -1;
>> + Â Â int nb;
>> + Â Â char vendor[16];
>> +
>> + Â Â cpuid(0, &lvl, &b, &c, &d);
>> + Â Â strncpy(&vendor[0], (char *)(&b), 4);
>> + Â Â strncpy(&vendor[4], (char *)(&d), 4);
>> + Â Â strncpy(&vendor[8], (char *)(&c), 4);
>> + Â Â vendor[12] = '\0';
>> +
>> + Â Â if (lvl >= 1) {
>> + Â Â Â Â Â Â cpuid(1, &a, &b, &c, &d);
>> +
>> + Â Â Â Â Â Â family = (a >> 8) & 0xf; Â/* bits 11 - 8 */
>> + Â Â Â Â Â Â model Â= (a >> 4) & 0xf; Â/* Bits Â7 - 4 */
>> +       step  = a & 0xf;
>> +
>> + Â Â Â Â Â Â /* extended family */
>> + Â Â Â Â Â Â if (family == 0xf)
>> + Â Â Â Â Â Â Â Â Â Â family += (a >> 20) & 0xff;
>> +
>> + Â Â Â Â Â Â /* extended model */
>> + Â Â Â Â Â Â if (family >= 0x6)
>> + Â Â Â Â Â Â Â Â Â Â model += ((a >> 16) & 0xf) << 4;
>> + Â Â }
>> + Â Â nb = snprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step);
>> +
>> + Â Â /* look for end marker to ensure the entire data fit */
>> + Â Â if (strchr(buffer, '$')) {
>> + Â Â Â Â Â Â buffer[nb-1] = '\0';
>> + Â Â Â Â Â Â return 0;
>> + Â Â }
>> + Â Â return -1;
>> +}
>> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
>> index 6b0519f..1817217 100644
>> --- a/tools/perf/builtin-record.c
>> +++ b/tools/perf/builtin-record.c
>> @@ -513,6 +513,19 @@ 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_CPUDESC);
>> + Â Â 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);
>> + Â Â perf_header__set_feat(&session->header, HEADER_CPUID);
>> +
>> Â Â Â /* 512 kiB: default amount of unprivileged mlocked memory */
>> Â Â Â if (mmap_pages == UINT_MAX)
>> Â Â Â Â Â Â Â mmap_pages = (512 * 1024) / page_size;
>> @@ -782,6 +795,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..269380d 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,875 @@ 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_cpudesc(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;
>> +}
>> +
>> +/*
>> + * default get_cpuid(): nothing gets recorded
>> + * actual implementation must be in arch/$(ARCH)/util/header.c
>> + */
>> +int __attribute__((weak)) get_cpuid(char *buffer __used, size_t sz __used)
>> +{
>> + Â Â return -1;
>> +}
>> +
>> +static int write_cpuid(int fd, struct perf_header *h __used,
>> + Â Â Â Â Â Â Â Â Â Âstruct perf_evlist *evlist __used)
>> +{
>> + Â Â char buffer[64];
>> + Â Â int ret;
>> +
>> + Â Â ret = get_cpuid(buffer, sizeof(buffer));
>> + Â Â if (!ret)
>> + Â Â Â Â Â Â goto write_it;
>> +
>> + Â Â return -1;
>> +write_it:
>> + Â Â return do_write_string(fd, buffer);
>> +}
>> +
>> +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_cpudesc(struct perf_header *ph, int fd, FILE *fp)
>> +{
>> + Â Â char *str = do_read_string(fd, ph);
>> + Â Â fprintf(fp, "# cpudesc : %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");
>> +}
>> +
>> +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);
>> +}
>> +
>> +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_CPUDESC, write_cpudesc, print_cpudesc),
>> + Â Â 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),
>> + Â Â FEAT_OP(HEADER_CPUID, write_cpuid, print_cpuid),
>> +};
>> +
>> +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 +1233,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 +1279,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 +1288,69 @@ 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_CPUDESC, &p, evlist);
>> + Â Â if (err)
>> + Â Â Â Â Â Â perf_header__clear_feat(header, HEADER_CPUDESC);
>> +
>> + Â Â 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);
>> +
>> + Â Â err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist);
>> + Â Â if (err)
>> + Â Â Â Â Â Â perf_header__clear_feat(header, HEADER_CPUID);
>>
>> Â Â Â 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 +1490,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 +1521,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 +1733,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 +1872,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..50209b0 100644
>> --- a/tools/perf/util/header.h
>> +++ b/tools/perf/util/header.h
>> @@ -12,6 +12,20 @@
>> Âenum {
>> Â Â Â HEADER_TRACE_INFO = 1,
>> Â Â Â HEADER_BUILD_ID,
>> +
>> + Â Â HEADER_HOSTNAME,
>> + Â Â HEADER_OSRELEASE,
>> + Â Â HEADER_ARCH,
>> + Â Â HEADER_CPUDESC,
>> + Â Â HEADER_NRCPUS,
>> + Â Â HEADER_EVENT_DESC,
>> + Â Â HEADER_CMDLINE,
>> + Â Â HEADER_VERSION,
>> + Â Â HEADER_CPU_TOPOLOGY,
>> + Â Â HEADER_TOTAL_MEM,
>> + Â Â HEADER_NUMA_TOPOLOGY,
>> + Â Â HEADER_CPUID,
>> +
>> Â Â Â HEADER_LAST_FEATURE,
>> Â};
>>
>> @@ -68,10 +82,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);
>> @@ -104,4 +123,10 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc,
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct perf_session *session);
>> Âint perf_event__process_build_id(union perf_event *event,
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct perf_session *session);
>> +
>> +/*
>> + * arch specific callback
>> + */
>> +int get_cpuid(char *buffer, size_t sz);
>> +
>> Â#endif /* __PERF_HEADER_H */
>> 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 */
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/