// Copied from kernel sources. See COPYING for license details.

#ifndef PERF_INTERNALS_H_
#define PERF_INTERNALS_H_

#include <linux/limits.h>
#include <stdint.h>

#include "perf_event.h"

namespace quipper {

// These typedefs are from tools/perf/util/types.h in the kernel.
typedef uint64_t           u64;
typedef int64_t            s64;
typedef unsigned int	   u32;
typedef signed int	   s32;
typedef unsigned short	   u16;
typedef signed short	   s16;
typedef unsigned char	   u8;
typedef signed char	   s8;

// The first 64 bits of the perf header, used as a perf data file ID tag.
const uint64_t kPerfMagic = 0x32454c4946524550LL;  // "PERFILE2" little-endian

// These data structures have been copied from the kernel. See files under
// tools/perf/util.

//
// From tools/perf/util/header.h
//

enum {
	HEADER_RESERVED		= 0,	/* always cleared */
	HEADER_FIRST_FEATURE	= 1,
	HEADER_TRACING_DATA	= 1,
	HEADER_BUILD_ID,

	HEADER_HOSTNAME,
	HEADER_OSRELEASE,
	HEADER_VERSION,
	HEADER_ARCH,
	HEADER_NRCPUS,
	HEADER_CPUDESC,
	HEADER_CPUID,
	HEADER_TOTAL_MEM,
	HEADER_CMDLINE,
	HEADER_EVENT_DESC,
	HEADER_CPU_TOPOLOGY,
	HEADER_NUMA_TOPOLOGY,
	HEADER_BRANCH_STACK,
	HEADER_PMU_MAPPINGS,
	HEADER_GROUP_DESC,
	HEADER_LAST_FEATURE,
	HEADER_FEAT_BITS	= 256,
};

struct perf_file_section {
	u64 offset;
	u64 size;
};

struct perf_file_attr {
	struct perf_event_attr	attr;
	struct perf_file_section	ids;
};

#define BITS_PER_BYTE           8
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

#define DECLARE_BITMAP(name,bits) \
	unsigned long name[BITS_TO_LONGS(bits)]

struct perf_file_header {
	u64				magic;
	u64				size;
	u64				attr_size;
	struct perf_file_section	attrs;
	struct perf_file_section	data;
	struct perf_file_section	event_types;
	DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
};

#undef BITS_PER_BYTE
#undef DIV_ROUND_UP
#undef BITS_TO_LONGS
#undef DECLARE_BITMAP

struct perf_pipe_file_header {
	u64				magic;
	u64				size;
};

//
// From tools/perf/util/event.h
//

struct mmap_event {
	struct perf_event_header header;
	u32 pid, tid;
	u64 start;
	u64 len;
	u64 pgoff;
	char filename[PATH_MAX];
};

struct mmap2_event {
	struct perf_event_header header;
	u32 pid, tid;
	u64 start;
	u64 len;
	u64 pgoff;
	u32 maj;
	u32 min;
	u64 ino;
	u64 ino_generation;
	u32 prot;
	u32 flags;
	char filename[PATH_MAX];
};

struct comm_event {
	struct perf_event_header header;
	u32 pid, tid;
	char comm[16];
};

struct fork_event {
	struct perf_event_header header;
	u32 pid, ppid;
	u32 tid, ptid;
	u64 time;
};

struct lost_event {
	struct perf_event_header header;
	u64 id;
	u64 lost;
};

/*
 * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
 */
struct read_event {
	struct perf_event_header header;
	u32 pid, tid;
	u64 value;
	u64 time_enabled;
	u64 time_running;
	u64 id;
};

struct throttle_event {
	struct perf_event_header header;
	u64 time;
	u64 id;
	u64 stream_id;
};

#define PERF_SAMPLE_MASK				\
	(PERF_SAMPLE_IP | PERF_SAMPLE_TID |		\
	 PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR |		\
	PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID |	\
	 PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD |		\
	 PERF_SAMPLE_IDENTIFIER)

/* perf sample has 16 bits size limit */
#define PERF_SAMPLE_MAX_SIZE (1 << 16)

struct sample_event {
	struct perf_event_header        header;
	u64 array[];
};

#if 0
// PERF_REGS_MAX is arch-dependent, so this is not a useful struct as-is.
struct regs_dump {
	u64 abi;
	u64 mask;
	u64 *regs;

	/* Cached values/mask filled by first register access. */
	u64 cache_regs[PERF_REGS_MAX];
	u64 cache_mask;
};
#endif

struct stack_dump {
	u16 offset;
	u64 size;
	char *data;
};

struct sample_read_value {
	u64 value;
	u64 id;
};

struct sample_read {
	u64 time_enabled;
	u64 time_running;
	union {
		struct {
			u64 nr;
			struct sample_read_value *values;
		} group;
		struct sample_read_value one;
	};
};

struct ip_callchain {
	u64 nr;
	u64 ips[0];
};

struct branch_flags {
	u64 mispred:1;
	u64 predicted:1;
	u64 in_tx:1;
	u64 abort:1;
	u64 reserved:60;
};

struct branch_entry {
	u64			from;
	u64			to;
	struct branch_flags	flags;
};

struct branch_stack {
	u64			nr;
	struct branch_entry	entries[0];
};

// All the possible fields of a perf sample.  This is not an actual data
// structure found in raw perf data, as each field may or may not be present in
// the data.
struct perf_sample {
	u64 ip;
	u32 pid, tid;
	u64 time;
	u64 addr;
	u64 id;
	u64 stream_id;
	u64 period;
	u64 weight;
	u64 transaction;
	u32 cpu;
	u32 raw_size;
	u64 data_src;
	u32 flags;
	u16 insn_len;
	void *raw_data;
	struct ip_callchain *callchain;
	struct branch_stack *branch_stack;
	//struct regs_dump  user_regs;  // See struct regs_dump above.
	struct stack_dump user_stack;
	struct {  // Copied from struct read_event.
		u64 time_enabled;
		u64 time_running;
		u64 id;
	} read;
	// TODO(dhsharp) replace struct above with:
	// struct sample_read read;

	perf_sample() : raw_data(NULL),
			callchain(NULL),
			branch_stack(NULL) {}
	~perf_sample() {
	  delete [] callchain;
	  delete [] branch_stack;
	  delete [] reinterpret_cast<char*>(raw_data);
	}
};

// Taken from tools/perf/util/include/linux/kernel.h
#define ALIGN(x,a)		__ALIGN_MASK(x,(typeof(x))(a)-1)
#define __ALIGN_MASK(x,mask)	(((x)+(mask))&~(mask))

// If this is changed, kBuildIDArraySize in perf_reader.h must also be changed.
#define BUILD_ID_SIZE 20

struct build_id_event {
	struct perf_event_header header;
	pid_t			 pid;
	u8			 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
	char			 filename[];
};

#undef ALIGN
#undef __ALIGN_MASK
#undef BUILD_ID_SIZE

enum perf_user_event_type { /* above any possible kernel type */
	PERF_RECORD_USER_TYPE_START		= 64,
	PERF_RECORD_HEADER_ATTR			= 64,
	PERF_RECORD_HEADER_EVENT_TYPE		= 65, /* depreceated */
	PERF_RECORD_HEADER_TRACING_DATA		= 66,
	PERF_RECORD_HEADER_BUILD_ID		= 67,
	PERF_RECORD_FINISHED_ROUND		= 68,
	/* Google-added pipe-mode event types: */
	PERF_RECORD_HEADER_HOSTNAME		= 69,
	PERF_RECORD_HEADER_OSRELEASE		= 70,
	PERF_RECORD_HEADER_VERSION		= 71,
	PERF_RECORD_HEADER_ARCH			= 72,
	PERF_RECORD_HEADER_NRCPUS		= 73,
	PERF_RECORD_HEADER_CPUDESC		= 74,
	PERF_RECORD_HEADER_CPUID		= 75,
	PERF_RECORD_HEADER_TOTAL_MEM		= 76,
	PERF_RECORD_HEADER_CMDLINE		= 77,
	PERF_RECORD_HEADER_EVENT_DESC		= 78,
	PERF_RECORD_HEADER_CPU_TOPOLOGY		= 79,
	PERF_RECORD_HEADER_NUMA_TOPOLOGY	= 80,
	PERF_RECORD_HEADER_BRANCH_STACK		= 81,
	PERF_RECORD_HEADER_PMU_MAPPINGS		= 82,
	PERF_RECORD_HEADER_GROUP_DESC		= 83,
	PERF_RECORD_HEADER_MAX
};

struct attr_event {
	struct perf_event_header header;
	struct perf_event_attr attr;
	u64 id[];
};

#define MAX_EVENT_NAME 64

struct perf_trace_event_type {
	u64	event_id;
	char	name[MAX_EVENT_NAME];
};

#undef MAX_EVENT_NAME

struct event_type_event {
	struct perf_event_header header;
	struct perf_trace_event_type event_type;
};

struct tracing_data_event {
	struct perf_event_header header;
	u32 size;
};

union perf_event {
	struct perf_event_header	header;
	struct mmap_event		mmap;
	struct mmap2_event		mmap2;
	struct comm_event		comm;
	struct fork_event		fork;
	struct lost_event		lost;
	struct read_event		read;
	struct throttle_event		throttle;
	struct sample_event		sample;
	struct attr_event		attr;
	struct event_type_event		event_type;
	struct tracing_data_event	tracing_data;
	struct build_id_event		build_id;
};

typedef perf_event event_t;

}  // namespace quipper

#endif /*PERF_INTERNALS_H_*/
