summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/hv/hv_kvp_daemon.c458
-rw-r--r--tools/include/tools/be_byteshift.h70
-rw-r--r--tools/include/tools/le_byteshift.h70
-rw-r--r--tools/perf/Documentation/Makefile86
-rw-r--r--tools/perf/Documentation/perf-lock.txt20
-rw-r--r--tools/perf/Documentation/perf-record.txt38
-rw-r--r--tools/perf/Documentation/perf-report.txt10
-rw-r--r--tools/perf/Documentation/perf-script.txt5
-rw-r--r--tools/perf/Documentation/perf-stat.txt4
-rw-r--r--tools/perf/Documentation/perf-top.txt8
-rw-r--r--tools/perf/MANIFEST1
-rw-r--r--tools/perf/Makefile26
-rw-r--r--tools/perf/bench/bench.h1
-rw-r--r--tools/perf/bench/mem-memcpy-x86-64-asm-def.h8
-rw-r--r--tools/perf/bench/mem-memcpy-x86-64-asm.S6
-rw-r--r--tools/perf/bench/mem-memcpy.c12
-rw-r--r--tools/perf/bench/mem-memset-arch.h12
-rw-r--r--tools/perf/bench/mem-memset-x86-64-asm-def.h12
-rw-r--r--tools/perf/bench/mem-memset-x86-64-asm.S13
-rw-r--r--tools/perf/bench/mem-memset.c297
-rw-r--r--tools/perf/builtin-bench.c3
-rw-r--r--tools/perf/builtin-lock.c4
-rw-r--r--tools/perf/builtin-probe.c12
-rw-r--r--tools/perf/builtin-record.c152
-rw-r--r--tools/perf/builtin-report.c178
-rw-r--r--tools/perf/builtin-script.c80
-rw-r--r--tools/perf/builtin-stat.c41
-rw-r--r--tools/perf/builtin-test.c188
-rw-r--r--tools/perf/builtin-top.c45
-rw-r--r--tools/perf/perf.h26
-rwxr-xr-xtools/perf/python/twatch.py2
-rw-r--r--tools/perf/util/annotate.c2
-rw-r--r--tools/perf/util/bitmap.c10
-rw-r--r--tools/perf/util/cpumap.c11
-rw-r--r--tools/perf/util/cpumap.h4
-rw-r--r--tools/perf/util/ctype.c2
-rw-r--r--tools/perf/util/debugfs.c141
-rw-r--r--tools/perf/util/debugfs.h6
-rw-r--r--tools/perf/util/event.h1
-rw-r--r--tools/perf/util/evlist.c17
-rw-r--r--tools/perf/util/evlist.h9
-rw-r--r--tools/perf/util/evsel.c22
-rw-r--r--tools/perf/util/header.c588
-rw-r--r--tools/perf/util/header.h3
-rw-r--r--tools/perf/util/hist.c122
-rw-r--r--tools/perf/util/hist.h13
-rw-r--r--tools/perf/util/include/asm/dwarf2.h4
-rw-r--r--tools/perf/util/include/linux/bitmap.h11
-rw-r--r--tools/perf/util/include/linux/bitops.h2
-rw-r--r--tools/perf/util/map.c15
-rw-r--r--tools/perf/util/map.h1
-rw-r--r--tools/perf/util/probe-event.c33
-rw-r--r--tools/perf/util/probe-finder.c1
-rw-r--r--tools/perf/util/python-ext-sources19
-rw-r--r--tools/perf/util/python.c10
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c1
-rw-r--r--tools/perf/util/session.c126
-rw-r--r--tools/perf/util/session.h6
-rw-r--r--tools/perf/util/setup.py8
-rw-r--r--tools/perf/util/sort.c287
-rw-r--r--tools/perf/util/sort.h11
-rw-r--r--tools/perf/util/symbol.c24
-rw-r--r--tools/perf/util/symbol.h24
-rw-r--r--tools/perf/util/sysfs.c60
-rw-r--r--tools/perf/util/sysfs.h6
-rw-r--r--tools/perf/util/thread_map.c237
-rw-r--r--tools/perf/util/thread_map.h11
-rw-r--r--tools/perf/util/top.c13
-rw-r--r--tools/perf/util/top.h6
-rw-r--r--tools/perf/util/trace-event-parse.c13
-rw-r--r--tools/perf/util/trace-event-read.c1
-rw-r--r--tools/perf/util/trace-event-scripting.c1
-rw-r--r--tools/perf/util/ui/browsers/annotate.c18
-rw-r--r--tools/perf/util/ui/browsers/hists.c105
-rw-r--r--tools/perf/util/ui/browsers/map.c2
-rw-r--r--tools/perf/util/usage.c39
-rw-r--r--tools/perf/util/util.c2
-rw-r--r--tools/perf/util/util.h6
-rw-r--r--tools/power/cpupower/Makefile93
-rw-r--r--tools/power/cpupower/bench/Makefile23
-rw-r--r--tools/power/cpupower/debug/i386/Makefile40
-rw-r--r--tools/power/cpupower/debug/x86_64/Makefile26
-rw-r--r--tools/power/cpupower/man/cpupower-frequency-info.14
-rw-r--r--tools/power/cpupower/man/cpupower-frequency-set.14
-rw-r--r--tools/power/cpupower/man/cpupower-idle-info.190
-rw-r--r--tools/power/cpupower/man/cpupower-monitor.12
-rw-r--r--tools/power/cpupower/utils/cpuidle-info.c12
-rw-r--r--tools/power/cpupower/utils/helpers/amd.c4
-rw-r--r--tools/power/cpupower/utils/helpers/helpers.h11
-rw-r--r--tools/power/cpupower/utils/helpers/pci.c35
-rw-r--r--tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c25
-rwxr-xr-xtools/testing/ktest/ktest.pl58
-rw-r--r--tools/testing/ktest/sample.conf14
-rw-r--r--tools/testing/selftests/Makefile7
-rw-r--r--tools/testing/selftests/breakpoints/Makefile7
-rw-r--r--tools/testing/selftests/run_tests8
-rw-r--r--tools/testing/selftests/vm/Makefile14
-rw-r--r--tools/testing/selftests/vm/hugepage-mmap.c92
-rw-r--r--tools/testing/selftests/vm/hugepage-shm.c100
-rw-r--r--tools/testing/selftests/vm/map_hugetlb.c79
-rw-r--r--tools/testing/selftests/vm/run_vmtests77
-rw-r--r--tools/usb/Makefile2
-rw-r--r--tools/usb/ffs-test.c31
-rw-r--r--tools/usb/testusb.c2
-rw-r--r--tools/virtio/linux/hrtimer.h0
-rw-r--r--tools/virtio/linux/module.h0
-rw-r--r--tools/virtio/linux/virtio.h3
-rw-r--r--tools/vm/Makefile11
-rw-r--r--tools/vm/page-types.c1102
-rw-r--r--tools/vm/slabinfo.c (renamed from tools/slub/slabinfo.c)0
110 files changed, 4994 insertions, 924 deletions
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 11224eddcdc..146fd6147e8 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -34,21 +34,13 @@
#include <errno.h>
#include <arpa/inet.h>
#include <linux/connector.h>
+#include <linux/hyperv.h>
#include <linux/netlink.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <syslog.h>
-
-/*
- * KYS: TODO. Need to register these in the kernel.
- *
- * The following definitions are shared with the in-kernel component; do not
- * change any of this without making the corresponding changes in
- * the KVP kernel component.
- */
-#define CN_KVP_IDX 0x9 /* MSFT KVP functionality */
-#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */
-#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */
+#include <sys/stat.h>
+#include <fcntl.h>
/*
* KVP protocol: The user mode component first registers with the
@@ -60,25 +52,8 @@
* We use this infrastructure for also supporting queries from user mode
* application for state that may be maintained in the KVP kernel component.
*
- * XXXKYS: Have a shared header file between the user and kernel (TODO)
*/
-enum kvp_op {
- KVP_REGISTER = 0, /* Register the user mode component*/
- KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/
- KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/
- KVP_USER_GET, /*User is requesting the value for the specified key*/
- KVP_USER_SET /*User is providing the value for the specified key*/
-};
-
-#define HV_KVP_EXCHANGE_MAX_KEY_SIZE 512
-#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE 2048
-
-struct hv_ku_msg {
- __u32 kvp_index;
- __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */
- __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */
-};
enum key_index {
FullyQualifiedDomainName = 0,
@@ -93,10 +68,6 @@ enum key_index {
ProcessorArchitecture
};
-/*
- * End of shared definitions.
- */
-
static char kvp_send_buffer[4096];
static char kvp_recv_buffer[4096];
static struct sockaddr_nl addr;
@@ -109,6 +80,345 @@ static char *os_build;
static char *lic_version;
static struct utsname uts_buf;
+
+#define MAX_FILE_NAME 100
+#define ENTRIES_PER_BLOCK 50
+
+struct kvp_record {
+ __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+ __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
+};
+
+struct kvp_file_state {
+ int fd;
+ int num_blocks;
+ struct kvp_record *records;
+ int num_records;
+ __u8 fname[MAX_FILE_NAME];
+};
+
+static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
+
+static void kvp_acquire_lock(int pool)
+{
+ struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
+ fl.l_pid = getpid();
+
+ if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {
+ syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool);
+ exit(-1);
+ }
+}
+
+static void kvp_release_lock(int pool)
+{
+ struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
+ fl.l_pid = getpid();
+
+ if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {
+ perror("fcntl");
+ syslog(LOG_ERR, "Failed to release the lock pool: %d", pool);
+ exit(-1);
+ }
+}
+
+static void kvp_update_file(int pool)
+{
+ FILE *filep;
+ size_t bytes_written;
+
+ /*
+ * We are going to write our in-memory registry out to
+ * disk; acquire the lock first.
+ */
+ kvp_acquire_lock(pool);
+
+ filep = fopen(kvp_file_info[pool].fname, "w");
+ if (!filep) {
+ kvp_release_lock(pool);
+ syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
+ exit(-1);
+ }
+
+ bytes_written = fwrite(kvp_file_info[pool].records,
+ sizeof(struct kvp_record),
+ kvp_file_info[pool].num_records, filep);
+
+ fflush(filep);
+ kvp_release_lock(pool);
+}
+
+static void kvp_update_mem_state(int pool)
+{
+ FILE *filep;
+ size_t records_read = 0;
+ struct kvp_record *record = kvp_file_info[pool].records;
+ struct kvp_record *readp;
+ int num_blocks = kvp_file_info[pool].num_blocks;
+ int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
+
+ kvp_acquire_lock(pool);
+
+ filep = fopen(kvp_file_info[pool].fname, "r");
+ if (!filep) {
+ kvp_release_lock(pool);
+ syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
+ exit(-1);
+ }
+ while (!feof(filep)) {
+ readp = &record[records_read];
+ records_read += fread(readp, sizeof(struct kvp_record),
+ ENTRIES_PER_BLOCK * num_blocks,
+ filep);
+
+ if (!feof(filep)) {
+ /*
+ * We have more data to read.
+ */
+ num_blocks++;
+ record = realloc(record, alloc_unit * num_blocks);
+
+ if (record == NULL) {
+ syslog(LOG_ERR, "malloc failed");
+ exit(-1);
+ }
+ continue;
+ }
+ break;
+ }
+
+ kvp_file_info[pool].num_blocks = num_blocks;
+ kvp_file_info[pool].records = record;
+ kvp_file_info[pool].num_records = records_read;
+
+ kvp_release_lock(pool);
+}
+static int kvp_file_init(void)
+{
+ int ret, fd;
+ FILE *filep;
+ size_t records_read;
+ __u8 *fname;
+ struct kvp_record *record;
+ struct kvp_record *readp;
+ int num_blocks;
+ int i;
+ int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
+
+ if (access("/var/opt/hyperv", F_OK)) {
+ if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) {
+ syslog(LOG_ERR, " Failed to create /var/opt/hyperv");
+ exit(-1);
+ }
+ }
+
+ for (i = 0; i < KVP_POOL_COUNT; i++) {
+ fname = kvp_file_info[i].fname;
+ records_read = 0;
+ num_blocks = 1;
+ sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i);
+ fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
+
+ if (fd == -1)
+ return 1;
+
+
+ filep = fopen(fname, "r");
+ if (!filep)
+ return 1;
+
+ record = malloc(alloc_unit * num_blocks);
+ if (record == NULL) {
+ fclose(filep);
+ return 1;
+ }
+ while (!feof(filep)) {
+ readp = &record[records_read];
+ records_read += fread(readp, sizeof(struct kvp_record),
+ ENTRIES_PER_BLOCK,
+ filep);
+
+ if (!feof(filep)) {
+ /*
+ * We have more data to read.
+ */
+ num_blocks++;
+ record = realloc(record, alloc_unit *
+ num_blocks);
+ if (record == NULL) {
+ fclose(filep);
+ return 1;
+ }
+ continue;
+ }
+ break;
+ }
+ kvp_file_info[i].fd = fd;
+ kvp_file_info[i].num_blocks = num_blocks;
+ kvp_file_info[i].records = record;
+ kvp_file_info[i].num_records = records_read;
+ fclose(filep);
+
+ }
+
+ return 0;
+}
+
+static int kvp_key_delete(int pool, __u8 *key, int key_size)
+{
+ int i;
+ int j, k;
+ int num_records;
+ struct kvp_record *record;
+
+ /*
+ * First update the in-memory state.
+ */
+ kvp_update_mem_state(pool);
+
+ num_records = kvp_file_info[pool].num_records;
+ record = kvp_file_info[pool].records;
+
+ for (i = 0; i < num_records; i++) {
+ if (memcmp(key, record[i].key, key_size))
+ continue;
+ /*
+ * Found a match; just move the remaining
+ * entries up.
+ */
+ if (i == num_records) {
+ kvp_file_info[pool].num_records--;
+ kvp_update_file(pool);
+ return 0;
+ }
+
+ j = i;
+ k = j + 1;
+ for (; k < num_records; k++) {
+ strcpy(record[j].key, record[k].key);
+ strcpy(record[j].value, record[k].value);
+ j++;
+ }
+
+ kvp_file_info[pool].num_records--;
+ kvp_update_file(pool);
+ return 0;
+ }
+ return 1;
+}
+
+static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value,
+ int value_size)
+{
+ int i;
+ int j, k;
+ int num_records;
+ struct kvp_record *record;
+ int num_blocks;
+
+ if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
+ (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
+ return 1;
+
+ /*
+ * First update the in-memory state.
+ */
+ kvp_update_mem_state(pool);
+
+ num_records = kvp_file_info[pool].num_records;
+ record = kvp_file_info[pool].records;
+ num_blocks = kvp_file_info[pool].num_blocks;
+
+ for (i = 0; i < num_records; i++) {
+ if (memcmp(key, record[i].key, key_size))
+ continue;
+ /*
+ * Found a match; just update the value -
+ * this is the modify case.
+ */
+ memcpy(record[i].value, value, value_size);
+ kvp_update_file(pool);
+ return 0;
+ }
+
+ /*
+ * Need to add a new entry;
+ */
+ if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) {
+ /* Need to allocate a larger array for reg entries. */
+ record = realloc(record, sizeof(struct kvp_record) *
+ ENTRIES_PER_BLOCK * (num_blocks + 1));
+
+ if (record == NULL)
+ return 1;
+ kvp_file_info[pool].num_blocks++;
+
+ }
+ memcpy(record[i].value, value, value_size);
+ memcpy(record[i].key, key, key_size);
+ kvp_file_info[pool].records = record;
+ kvp_file_info[pool].num_records++;
+ kvp_update_file(pool);
+ return 0;
+}
+
+static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value,
+ int value_size)
+{
+ int i;
+ int num_records;
+ struct kvp_record *record;
+
+ if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
+ (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
+ return 1;
+
+ /*
+ * First update the in-memory state.
+ */
+ kvp_update_mem_state(pool);
+
+ num_records = kvp_file_info[pool].num_records;
+ record = kvp_file_info[pool].records;
+
+ for (i = 0; i < num_records; i++) {
+ if (memcmp(key, record[i].key, key_size))
+ continue;
+ /*
+ * Found a match; just copy the value out.
+ */
+ memcpy(value, record[i].value, value_size);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
+ __u8 *value, int value_size)
+{
+ struct kvp_record *record;
+
+ /*
+ * First update our in-memory database.
+ */
+ kvp_update_mem_state(pool);
+ record = kvp_file_info[pool].records;
+
+ if (index >= kvp_file_info[pool].num_records) {
+ /*
+ * This is an invalid index; terminate enumeration;
+ * - a NULL value will do the trick.
+ */
+ strcpy(value, "");
+ return;
+ }
+
+ memcpy(key, record[index].key, key_size);
+ memcpy(value, record[index].value, value_size);
+}
+
+
void kvp_get_os_info(void)
{
FILE *file;
@@ -332,7 +642,7 @@ int main(void)
struct pollfd pfd;
struct nlmsghdr *incoming_msg;
struct cn_msg *incoming_cn_msg;
- struct hv_ku_msg *hv_msg;
+ struct hv_kvp_msg *hv_msg;
char *p;
char *key_value;
char *key_name;
@@ -345,6 +655,11 @@ int main(void)
*/
kvp_get_os_info();
+ if (kvp_file_init()) {
+ syslog(LOG_ERR, "Failed to initialize the pools");
+ exit(-1);
+ }
+
fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (fd < 0) {
syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
@@ -370,9 +685,11 @@ int main(void)
message = (struct cn_msg *)kvp_send_buffer;
message->id.idx = CN_KVP_IDX;
message->id.val = CN_KVP_VAL;
- message->seq = KVP_REGISTER;
+
+ hv_msg = (struct hv_kvp_msg *)message->data;
+ hv_msg->kvp_hdr.operation = KVP_OP_REGISTER;
message->ack = 0;
- message->len = 0;
+ message->len = sizeof(struct hv_kvp_msg);
len = netlink_send(fd, message);
if (len < 0) {
@@ -398,14 +715,15 @@ int main(void)
incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
+ hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
- switch (incoming_cn_msg->seq) {
- case KVP_REGISTER:
+ switch (hv_msg->kvp_hdr.operation) {
+ case KVP_OP_REGISTER:
/*
* Driver is registering with us; stash away the version
* information.
*/
- p = (char *)incoming_cn_msg->data;
+ p = (char *)hv_msg->body.kvp_register.version;
lic_version = malloc(strlen(p) + 1);
if (lic_version) {
strcpy(lic_version, p);
@@ -416,17 +734,65 @@ int main(void)
}
continue;
- case KVP_KERNEL_GET:
+ /*
+ * The current protocol with the kernel component uses a
+ * NULL key name to pass an error condition.
+ * For the SET, GET and DELETE operations,
+ * use the existing protocol to pass back error.
+ */
+
+ case KVP_OP_SET:
+ if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool,
+ hv_msg->body.kvp_set.data.key,
+ hv_msg->body.kvp_set.data.key_size,
+ hv_msg->body.kvp_set.data.value,
+ hv_msg->body.kvp_set.data.value_size))
+ strcpy(hv_msg->body.kvp_set.data.key, "");
+ break;
+
+ case KVP_OP_GET:
+ if (kvp_get_value(hv_msg->kvp_hdr.pool,
+ hv_msg->body.kvp_set.data.key,
+ hv_msg->body.kvp_set.data.key_size,
+ hv_msg->body.kvp_set.data.value,
+ hv_msg->body.kvp_set.data.value_size))
+ strcpy(hv_msg->body.kvp_set.data.key, "");
+ break;
+
+ case KVP_OP_DELETE:
+ if (kvp_key_delete(hv_msg->kvp_hdr.pool,
+ hv_msg->body.kvp_delete.key,
+ hv_msg->body.kvp_delete.key_size))
+ strcpy(hv_msg->body.kvp_delete.key, "");
break;
+
default:
- continue;
+ break;
+ }
+
+ if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE)
+ goto kvp_done;
+
+ /*
+ * If the pool is KVP_POOL_AUTO, dynamically generate
+ * both the key and the value; if not read from the
+ * appropriate pool.
+ */
+ if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) {
+ kvp_pool_enumerate(hv_msg->kvp_hdr.pool,
+ hv_msg->body.kvp_enum_data.index,
+ hv_msg->body.kvp_enum_data.data.key,
+ HV_KVP_EXCHANGE_MAX_KEY_SIZE,
+ hv_msg->body.kvp_enum_data.data.value,
+ HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
+ goto kvp_done;
}
- hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data;
- key_name = (char *)hv_msg->kvp_key;
- key_value = (char *)hv_msg->kvp_value;
+ hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
+ key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
+ key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
- switch (hv_msg->kvp_index) {
+ switch (hv_msg->body.kvp_enum_data.index) {
case FullyQualifiedDomainName:
kvp_get_domain_name(key_value,
HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
@@ -483,12 +849,12 @@ int main(void)
* already in the receive buffer. Update the cn_msg header to
* reflect the key value that has been added to the message
*/
+kvp_done:
incoming_cn_msg->id.idx = CN_KVP_IDX;
incoming_cn_msg->id.val = CN_KVP_VAL;
- incoming_cn_msg->seq = KVP_USER_SET;
incoming_cn_msg->ack = 0;
- incoming_cn_msg->len = sizeof(struct hv_ku_msg);
+ incoming_cn_msg->len = sizeof(struct hv_kvp_msg);
len = netlink_send(fd, incoming_cn_msg);
if (len < 0) {
diff --git a/tools/include/tools/be_byteshift.h b/tools/include/tools/be_byteshift.h
new file mode 100644
index 00000000000..f4912e2668b
--- /dev/null
+++ b/tools/include/tools/be_byteshift.h
@@ -0,0 +1,70 @@
+#ifndef _TOOLS_BE_BYTESHIFT_H
+#define _TOOLS_BE_BYTESHIFT_H
+
+#include <linux/types.h>
+
+static inline __u16 __get_unaligned_be16(const __u8 *p)
+{
+ return p[0] << 8 | p[1];
+}
+
+static inline __u32 __get_unaligned_be32(const __u8 *p)
+{
+ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+}
+
+static inline __u64 __get_unaligned_be64(const __u8 *p)
+{
+ return (__u64)__get_unaligned_be32(p) << 32 |
+ __get_unaligned_be32(p + 4);
+}
+
+static inline void __put_unaligned_be16(__u16 val, __u8 *p)
+{
+ *p++ = val >> 8;
+ *p++ = val;
+}
+
+static inline void __put_unaligned_be32(__u32 val, __u8 *p)
+{
+ __put_unaligned_be16(val >> 16, p);
+ __put_unaligned_be16(val, p + 2);
+}
+
+static inline void __put_unaligned_be64(__u64 val, __u8 *p)
+{
+ __put_unaligned_be32(val >> 32, p);
+ __put_unaligned_be32(val, p + 4);
+}
+
+static inline __u16 get_unaligned_be16(const void *p)
+{
+ return __get_unaligned_be16((const __u8 *)p);
+}
+
+static inline __u32 get_unaligned_be32(const void *p)
+{
+ return __get_unaligned_be32((const __u8 *)p);
+}
+
+static inline __u64 get_unaligned_be64(const void *p)
+{
+ return __get_unaligned_be64((const __u8 *)p);
+}
+
+static inline void put_unaligned_be16(__u16 val, void *p)
+{
+ __put_unaligned_be16(val, p);
+}
+
+static inline void put_unaligned_be32(__u32 val, void *p)
+{
+ __put_unaligned_be32(val, p);
+}
+
+static inline void put_unaligned_be64(__u64 val, void *p)
+{
+ __put_unaligned_be64(val, p);
+}
+
+#endif /* _TOOLS_BE_BYTESHIFT_H */
diff --git a/tools/include/tools/le_byteshift.h b/tools/include/tools/le_byteshift.h
new file mode 100644
index 00000000000..c99d45a68bd
--- /dev/null
+++ b/tools/include/tools/le_byteshift.h
@@ -0,0 +1,70 @@
+#ifndef _TOOLS_LE_BYTESHIFT_H
+#define _TOOLS_LE_BYTESHIFT_H
+
+#include <linux/types.h>
+
+static inline __u16 __get_unaligned_le16(const __u8 *p)
+{
+ return p[0] | p[1] << 8;
+}
+
+static inline __u32 __get_unaligned_le32(const __u8 *p)
+{
+ return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+}
+
+static inline __u64 __get_unaligned_le64(const __u8 *p)
+{
+ return (__u64)__get_unaligned_le32(p + 4) << 32 |
+ __get_unaligned_le32(p);
+}
+
+static inline void __put_unaligned_le16(__u16 val, __u8 *p)
+{
+ *p++ = val;
+ *p++ = val >> 8;
+}
+
+static inline void __put_unaligned_le32(__u32 val, __u8 *p)
+{
+ __put_unaligned_le16(val >> 16, p + 2);
+ __put_unaligned_le16(val, p);
+}
+
+static inline void __put_unaligned_le64(__u64 val, __u8 *p)
+{
+ __put_unaligned_le32(val >> 32, p + 4);
+ __put_unaligned_le32(val, p);
+}
+
+static inline __u16 get_unaligned_le16(const void *p)
+{
+ return __get_unaligned_le16((const __u8 *)p);
+}
+
+static inline __u32 get_unaligned_le32(const void *p)
+{
+ return __get_unaligned_le32((const __u8 *)p);
+}
+
+static inline __u64 get_unaligned_le64(const void *p)
+{
+ return __get_unaligned_le64((const __u8 *)p);
+}
+
+static inline void put_unaligned_le16(__u16 val, void *p)
+{
+ __put_unaligned_le16(val, p);
+}
+
+static inline void put_unaligned_le32(__u32 val, void *p)
+{
+ __put_unaligned_le32(val, p);
+}
+
+static inline void put_unaligned_le64(__u64 val, void *p)
+{
+ __put_unaligned_le64(val, p);
+}
+
+#endif /* _TOOLS_LE_BYTESHIFT_H */
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile
index 4626a398836..ca600e09c8d 100644
--- a/tools/perf/Documentation/Makefile
+++ b/tools/perf/Documentation/Makefile
@@ -1,3 +1,10 @@
+OUTPUT := ./
+ifeq ("$(origin O)", "command line")
+ ifneq ($(O),)
+ OUTPUT := $(O)/
+ endif
+endif
+
MAN1_TXT= \
$(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
$(wildcard perf-*.txt)) \
@@ -6,10 +13,11 @@ MAN5_TXT=
MAN7_TXT=
MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
-MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
-MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))
+_MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
+_MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))
-DOC_HTML=$(MAN_HTML)
+MAN_XML=$(addprefix $(OUTPUT),$(_MAN_XML))
+MAN_HTML=$(addprefix $(OUTPUT),$(_MAN_HTML))
ARTICLES =
# with their own formatting rules.
@@ -18,11 +26,17 @@ API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica
SP_ARTICLES += $(API_DOCS)
SP_ARTICLES += technical/api-index
-DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
+_DOC_HTML = $(_MAN_HTML)
+_DOC_HTML+=$(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
+DOC_HTML=$(addprefix $(OUTPUT),$(_DOC_HTML))
-DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
-DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT))
-DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
+_DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
+_DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT))
+_DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
+
+DOC_MAN1=$(addprefix $(OUTPUT),$(_DOC_MAN1))
+DOC_MAN5=$(addprefix $(OUTPUT),$(_DOC_MAN5))
+DOC_MAN7=$(addprefix $(OUTPUT),$(_DOC_MAN7))
# Make the path relative to DESTDIR, not prefix
ifndef DESTDIR
@@ -150,9 +164,9 @@ man1: $(DOC_MAN1)
man5: $(DOC_MAN5)
man7: $(DOC_MAN7)
-info: perf.info perfman.info
+info: $(OUTPUT)perf.info $(OUTPUT)perfman.info
-pdf: user-manual.pdf
+pdf: $(OUTPUT)user-manual.pdf
install: install-man
@@ -166,7 +180,7 @@ install-man: man
install-info: info
$(INSTALL) -d -m 755 $(DESTDIR)$(infodir)
- $(INSTALL) -m 644 perf.info perfman.info $(DESTDIR)$(infodir)
+ $(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir)
if test -r $(DESTDIR)$(infodir)/dir; then \
$(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perf.info ;\
$(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perfman.info ;\
@@ -176,7 +190,7 @@ install-info: info
install-pdf: pdf
$(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir)
- $(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir)
+ $(INSTALL) -m 644 $(OUTPUT)user-manual.pdf $(DESTDIR)$(pdfdir)
#install-html: html
# '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
@@ -189,14 +203,14 @@ install-pdf: pdf
#
# Determine "include::" file references in asciidoc files.
#
-doc.dep : $(wildcard *.txt) build-docdep.perl
+$(OUTPUT)doc.dep : $(wildcard *.txt) build-docdep.perl
$(QUIET_GEN)$(RM) $@+ $@ && \
$(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \
mv $@+ $@
--include doc.dep
+-include $(OUPTUT)doc.dep
-cmds_txt = cmds-ancillaryinterrogators.txt \
+_cmds_txt = cmds-ancillaryinterrogators.txt \
cmds-ancillarymanipulators.txt \
cmds-mainporcelain.txt \
cmds-plumbinginterrogators.txt \
@@ -205,32 +219,36 @@ cmds_txt = cmds-ancillaryinterrogators.txt \
cmds-synchelpers.txt \
cmds-purehelpers.txt \
cmds-foreignscminterface.txt
+cmds_txt=$(addprefix $(OUTPUT),$(_cmds_txt))
-$(cmds_txt): cmd-list.made
+$(cmds_txt): $(OUTPUT)cmd-list.made
-cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
+$(OUTPUT)cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT)
$(QUIET_GEN)$(RM) $@ && \
$(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \
date >$@
clean:
- $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
- $(RM) *.texi *.texi+ *.texi++ perf.info perfman.info
- $(RM) howto-index.txt howto/*.html doc.dep
- $(RM) technical/api-*.html technical/api-index.txt
- $(RM) $(cmds_txt) *.made
-
-$(MAN_HTML): %.html : %.txt
+ $(RM) $(MAN_XML) $(addsuffix +,$(MAN_XML))
+ $(RM) $(MAN_HTML) $(addsuffix +,$(MAN_HTML))
+ $(RM) $(DOC_HTML) $(DOC_MAN1) $(DOC_MAN5) $(DOC_MAN7)
+ $(RM) $(OUTPUT)*.texi $(OUTPUT)*.texi+ $(OUTPUT)*.texi++
+ $(RM) $(OUTPUT)perf.info $(OUTPUT)perfman.info
+ $(RM) $(OUTPUT)howto-index.txt $(OUTPUT)howto/*.html $(OUTPUT)doc.dep
+ $(RM) $(OUTPUT)technical/api-*.html $(OUTPUT)technical/api-index.txt
+ $(RM) $(cmds_txt) $(OUTPUT)*.made
+
+$(MAN_HTML): $(OUTPUT)%.html : %.txt
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \
$(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) -o $@+ $< && \
mv $@+ $@
-%.1 %.5 %.7 : %.xml
+$(OUTPUT)%.1 $(OUTPUT)%.5 $(OUTPUT)%.7 : $(OUTPUT)%.xml
$(QUIET_XMLTO)$(RM) $@ && \
- xmlto -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
+ xmlto -o $(OUTPUT) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
-%.xml : %.txt
+$(OUTPUT)%.xml : %.txt
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
$(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \
$(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) -o $@+ $< && \
@@ -239,25 +257,25 @@ $(MAN_HTML): %.html : %.txt
XSLT = docbook.xsl
XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
-user-manual.html: user-manual.xml
+$(OUTPUT)user-manual.html: $(OUTPUT)user-manual.xml
$(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
-perf.info: user-manual.texi
- $(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ user-manual.texi
+$(OUTPUT)perf.info: $(OUTPUT)user-manual.texi
+ $(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ $(OUTPUT)user-manual.texi
-user-manual.texi: user-manual.xml
+$(OUTPUT)user-manual.texi: $(OUTPUT)user-manual.xml
$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
- $(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \
+ $(DOCBOOK2X_TEXI) $(OUTPUT)user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \
$(PERL_PATH) fix-texi.perl <$@++ >$@+ && \
rm $@++ && \
mv $@+ $@
-user-manual.pdf: user-manual.xml
+$(OUTPUT)user-manual.pdf: $(OUTPUT)user-manual.xml
$(QUIET_DBLATEX)$(RM) $@+ $@ && \
$(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< && \
mv $@+ $@
-perfman.texi: $(MAN_XML) cat-texi.perl
+$(OUTPUT)perfman.texi: $(MAN_XML) cat-texi.perl
$(QUIET_DB2TEXI)$(RM) $@+ $@ && \
($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \
--to-stdout $(xml) &&) true) > $@++ && \
@@ -265,7 +283,7 @@ perfman.texi: $(MAN_XML) cat-texi.perl
rm $@++ && \
mv $@+ $@
-perfman.info: perfman.texi
+$(OUTPUT)perfman.info: $(OUTPUT)perfman.texi
$(QUIET_MAKEINFO)$(MAKEINFO) --no-split --no-validate $*.texi
$(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt
index d6b2a4f2108..c7f5f55634a 100644
--- a/tools/perf/Documentation/perf-lock.txt
+++ b/tools/perf/Documentation/perf-lock.txt
@@ -8,7 +8,7 @@ perf-lock - Analyze lock events
SYNOPSIS
--------
[verse]
-'perf lock' {record|report|trace}
+'perf lock' {record|report|script|info}
DESCRIPTION
-----------
@@ -20,10 +20,13 @@ and statistics with this 'perf lock' command.
produces the file "perf.data" which contains tracing
results of lock events.
- 'perf lock trace' shows raw lock events.
-
'perf lock report' reports statistical data.
+ 'perf lock script' shows raw lock events.
+
+ 'perf lock info' shows metadata like threads or addresses
+ of lock instances.
+
COMMON OPTIONS
--------------
@@ -47,6 +50,17 @@ REPORT OPTIONS
Sorting key. Possible values: acquired (default), contended,
wait_total, wait_max, wait_min.
+INFO OPTIONS
+------------
+
+-t::
+--threads::
+ dump thread list in perf.data
+
+-m::
+--map::
+ dump map of lock instances (address:name table)
+
SEE ALSO
--------
linkperf:perf[1]
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index 2937f7e14bb..a1386b2fff0 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -52,11 +52,15 @@ OPTIONS
-p::
--pid=::
- Record events on existing process ID.
+ Record events on existing process ID (comma separated list).
-t::
--tid=::
- Record events on existing thread ID.
+ Record events on existing thread ID (comma separated list).
+
+-u::
+--uid=::
+ Record events in threads owned by uid. Name or number.
-r::
--realtime=::
@@ -148,6 +152,36 @@ an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must ha
corresponding events, i.e., they always refer to events defined earlier on the command
line.
+-b::
+--branch-any::
+Enable taken branch stack sampling. Any type of taken branch may be sampled.
+This is a shortcut for --branch-filter any. See --branch-filter for more infos.
+
+-j::
+--branch-filter::
+Enable taken branch stack sampling. Each sample captures a series of consecutive
+taken branches. The number of branches captured with each sample depends on the
+underlying hardware, the type of branches of interest, and the executed code.
+It is possible to select the types of branches captured by enabling filters. The
+following filters are defined:
+
+ - any: any type of branches
+ - any_call: any function call or system call
+ - any_ret: any function return or system call return
+ - any_ind: any indirect branch
+ - u: only when the branch target is at the user level
+ - k: only when the branch target is in the kernel
+ - hv: only when the target is at the hypervisor level
+
++
+The option requires at least one branch type among any, any_call, any_ret, ind_call.
+The privilege levels may be ommitted, in which case, the privilege levels of the associated
+event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege
+levels are subject to permissions. When sampling on multiple events, branch stack sampling
+is enabled for all the sampling events. The sampled branch type is the same for all events.
+The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k
+Note that this feature may not be available on all processors.
+
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-list[1]
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 9b430e98712..87feeee8b90 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -153,6 +153,16 @@ OPTIONS
information which may be very large and thus may clutter the display.
It currently includes: cpu and numa topology of the host system.
+-b::
+--branch-stack::
+ Use the addresses of sampled taken branches instead of the instruction
+ address to build the histograms. To generate meaningful output, the
+ perf.data file must have been obtained using perf record -b or
+ perf record --branch-filter xxx where xxx is a branch filter option.
+ perf report is able to auto-detect whether a perf.data file contains
+ branch stacks and it will automatically switch to the branch view mode,
+ unless --no-branch-stack is used.
+
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-annotate[1]
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt
index 2f6cef43da2..e9cbfcddfa3 100644
--- a/tools/perf/Documentation/perf-script.txt
+++ b/tools/perf/Documentation/perf-script.txt
@@ -115,7 +115,7 @@ OPTIONS
-f::
--fields::
Comma separated list of fields to print. Options are:
- comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr.
+ comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff.
Field list can be prepended with the type, trace, sw or hw,
to indicate to which event type the field list applies.
e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace
@@ -200,6 +200,9 @@ OPTIONS
It currently includes: cpu and numa topology of the host system.
It can only be used with the perf script report mode.
+--show-kernel-path::
+ Try to resolve the path of [kernel.kallsyms]
+
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-script-perl[1],
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index 8966b9ab201..2fa173b5197 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -35,11 +35,11 @@ OPTIONS
child tasks do not inherit counters
-p::
--pid=<pid>::
- stat events on existing process id
+ stat events on existing process id (comma separated list)
-t::
--tid=<tid>::
- stat events on existing thread id
+ stat events on existing thread id (comma separated list)
-a::
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index b1a5bbbfebe..4a5680cb242 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -72,11 +72,15 @@ Default is to monitor all CPUS.
-p <pid>::
--pid=<pid>::
- Profile events on existing Process ID.
+ Profile events on existing Process ID (comma separated list).
-t <tid>::
--tid=<tid>::
- Profile events on existing thread ID.
+ Profile events on existing thread ID (comma separated list).
+
+-u::
+--uid=::
+ Record events in threads owned by uid. Name or number.
-r <priority>::
--realtime=<priority>::
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index 1078c5fadd5..5476bc0a1ea 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -9,6 +9,7 @@ lib/rbtree.c
include/linux/swab.h
arch/*/include/asm/unistd*.h
arch/*/lib/memcpy*.S
+arch/*/lib/memset*.S
include/linux/poison.h
include/linux/magic.h
include/linux/hw_breakpoint.h
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 8a4b9bccf8b..74fd7f89208 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -15,6 +15,16 @@ endif
# Define V to have a more verbose compile.
#
+# Define O to save output files in a separate directory.
+#
+# Define ARCH as name of target architecture if you want cross-builds.
+#
+# Define CROSS_COMPILE as prefix name of compiler if you want cross-builds.
+#
+# Define NO_LIBPERL to disable perl script extension.
+#
+# Define NO_LIBPYTHON to disable python script extension.
+#
# Define PYTHON to point to the python binary if the default
# `python' is not correct; for example: PYTHON=python2
#
@@ -32,6 +42,10 @@ endif
# Define NO_DWARF if you do not want debug-info analysis feature at all.
#
# Define WERROR=0 to disable treating any warnings as errors.
+#
+# Define NO_NEWT if you do not want TUI support.
+#
+# Define NO_DEMANGLE if you do not want C++ symbol demangling.
$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
@@ -61,7 +75,7 @@ ifeq ($(ARCH),x86_64)
ifeq (${IS_X86_64}, 1)
RAW_ARCH := x86_64
ARCH_CFLAGS := -DARCH_X86_64
- ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S
+ ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S
endif
endif
@@ -183,7 +197,10 @@ SCRIPT_SH += perf-archive.sh
grep-libs = $(filter -l%,$(1))
strip-libs = $(filter-out -l%,$(1))
-$(OUTPUT)python/perf.so: $(PYRF_OBJS)
+PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources)
+PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py
+
+$(OUTPUT)python/perf.so: $(PYRF_OBJS) $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
$(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \
--quiet build_ext; \
mkdir -p $(OUTPUT)python && \
@@ -258,6 +275,7 @@ LIB_H += util/callchain.h
LIB_H += util/build-id.h
LIB_H += util/debug.h
LIB_H += util/debugfs.h
+LIB_H += util/sysfs.h
LIB_H += util/event.h
LIB_H += util/evsel.h
LIB_H += util/evlist.h
@@ -304,6 +322,7 @@ LIB_OBJS += $(OUTPUT)util/build-id.o
LIB_OBJS += $(OUTPUT)util/config.o
LIB_OBJS += $(OUTPUT)util/ctype.o
LIB_OBJS += $(OUTPUT)util/debugfs.o
+LIB_OBJS += $(OUTPUT)util/sysfs.o
LIB_OBJS += $(OUTPUT)util/environment.o
LIB_OBJS += $(OUTPUT)util/event.o
LIB_OBJS += $(OUTPUT)util/evlist.o
@@ -361,8 +380,10 @@ BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o
BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o
ifeq ($(RAW_ARCH),x86_64)
BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o
+BUILTIN_OBJS += $(OUTPUT)bench/mem-memset-x86-64-asm.o
endif
BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o
+BUILTIN_OBJS += $(OUTPUT)bench/mem-memset.o
BUILTIN_OBJS += $(OUTPUT)builtin-diff.o
BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o
@@ -794,7 +815,6 @@ help:
@echo ' quick-install-html - install the html documentation quickly'
@echo ''
@echo 'Perf maintainer targets:'
- @echo ' distclean - alias to clean'
@echo ' clean - clean all binary objects and build output'
doc:
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
index f7781c6267c..a09bece6dad 100644
--- a/tools/perf/bench/bench.h
+++ b/tools/perf/bench/bench.h
@@ -4,6 +4,7 @@
extern int bench_sched_messaging(int argc, const char **argv, const char *prefix);
extern int bench_sched_pipe(int argc, const char **argv, const char *prefix);
extern int bench_mem_memcpy(int argc, const char **argv, const char *prefix __used);
+extern int bench_mem_memset(int argc, const char **argv, const char *prefix);
#define BENCH_FORMAT_DEFAULT_STR "default"
#define BENCH_FORMAT_DEFAULT 0
diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm-def.h b/tools/perf/bench/mem-memcpy-x86-64-asm-def.h
index d588b87696f..d66ab799b35 100644
--- a/tools/perf/bench/mem-memcpy-x86-64-asm-def.h
+++ b/tools/perf/bench/mem-memcpy-x86-64-asm-def.h
@@ -2,3 +2,11 @@
MEMCPY_FN(__memcpy,
"x86-64-unrolled",
"unrolled memcpy() in arch/x86/lib/memcpy_64.S")
+
+MEMCPY_FN(memcpy_c,
+ "x86-64-movsq",
+ "movsq-based memcpy() in arch/x86/lib/memcpy_64.S")
+
+MEMCPY_FN(memcpy_c_e,
+ "x86-64-movsb",
+ "movsb-based memcpy() in arch/x86/lib/memcpy_64.S")
diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm.S b/tools/perf/bench/mem-memcpy-x86-64-asm.S
index 185a96d66dd..fcd9cf00600 100644
--- a/tools/perf/bench/mem-memcpy-x86-64-asm.S
+++ b/tools/perf/bench/mem-memcpy-x86-64-asm.S
@@ -1,4 +1,8 @@
-
+#define memcpy MEMCPY /* don't hide glibc's memcpy() */
+#define altinstr_replacement text
+#define globl p2align 4; .globl
+#define Lmemcpy_c globl memcpy_c; memcpy_c
+#define Lmemcpy_c_e globl memcpy_c_e; memcpy_c_e
#include "../../../arch/x86/lib/memcpy_64.S"
/*
* We need to provide note.GNU-stack section, saying that we want
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c
index db82021f4b9..71557225bf9 100644
--- a/tools/perf/bench/mem-memcpy.c
+++ b/tools/perf/bench/mem-memcpy.c
@@ -5,7 +5,6 @@
*
* Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
*/
-#include <ctype.h>
#include "../perf.h"
#include "../util/util.h"
@@ -24,6 +23,7 @@
static const char *length_str = "1MB";
static const char *routine = "default";
+static int iterations = 1;
static bool use_clock;
static int clock_fd;
static bool only_prefault;
@@ -35,6 +35,8 @@ static const struct option options[] = {
"available unit: B, MB, GB (upper and lower)"),
OPT_STRING('r', "routine", &routine, "default",
"Specify routine to copy"),
+ OPT_INTEGER('i', "iterations", &iterations,
+ "repeat memcpy() invocation this number of times"),
OPT_BOOLEAN('c', "clock", &use_clock,
"Use CPU clock for measuring"),
OPT_BOOLEAN('o', "only-prefault", &only_prefault,
@@ -121,6 +123,7 @@ static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault)
{
u64 clock_start = 0ULL, clock_end = 0ULL;
void *src = NULL, *dst = NULL;
+ int i;
alloc_mem(&src, &dst, len);
@@ -128,7 +131,8 @@ static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault)
fn(dst, src, len);
clock_start = get_clock();
- fn(dst, src, len);
+ for (i = 0; i < iterations; ++i)
+ fn(dst, src, len);
clock_end = get_clock();
free(src);
@@ -140,6 +144,7 @@ static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault)
{
struct timeval tv_start, tv_end, tv_diff;
void *src = NULL, *dst = NULL;
+ int i;
alloc_mem(&src, &dst, len);
@@ -147,7 +152,8 @@ static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault)
fn(dst, src, len);
BUG_ON(gettimeofday(&tv_start, NULL));
- fn(dst, src, len);
+ for (i = 0; i < iterations; ++i)
+ fn(dst, src, len);
BUG_ON(gettimeofday(&tv_end, NULL));
timersub(&tv_end, &tv_start, &tv_diff);
diff --git a/tools/perf/bench/mem-memset-arch.h b/tools/perf/bench/mem-memset-arch.h
new file mode 100644
index 00000000000..a040fa77665
--- /dev/null
+++ b/tools/perf/bench/mem-memset-arch.h
@@ -0,0 +1,12 @@
+
+#ifdef ARCH_X86_64
+
+#define MEMSET_FN(fn, name, desc) \
+ extern void *fn(void *, int, size_t);
+
+#include "mem-memset-x86-64-asm-def.h"
+
+#undef MEMSET_FN
+
+#endif
+
diff --git a/tools/perf/bench/mem-memset-x86-64-asm-def.h b/tools/perf/bench/mem-memset-x86-64-asm-def.h
new file mode 100644
index 00000000000..a71dff97c1f
--- /dev/null
+++ b/tools/perf/bench/mem-memset-x86-64-asm-def.h
@@ -0,0 +1,12 @@
+
+MEMSET_FN(__memset,
+ "x86-64-unrolled",
+ "unrolled memset() in arch/x86/lib/memset_64.S")
+
+MEMSET_FN(memset_c,
+ "x86-64-stosq",
+ "movsq-based memset() in arch/x86/lib/memset_64.S")
+
+MEMSET_FN(memset_c_e,
+ "x86-64-stosb",
+ "movsb-based memset() in arch/x86/lib/memset_64.S")
diff --git a/tools/perf/bench/mem-memset-x86-64-asm.S b/tools/perf/bench/mem-memset-x86-64-asm.S
new file mode 100644
index 00000000000..9e5af89ed13
--- /dev/null
+++ b/tools/perf/bench/mem-memset-x86-64-asm.S
@@ -0,0 +1,13 @@
+#define memset MEMSET /* don't hide glibc's memset() */
+#define altinstr_replacement text
+#define globl p2align 4; .globl
+#define Lmemset_c globl memset_c; memset_c
+#define Lmemset_c_e globl memset_c_e; memset_c_e
+#include "../../../arch/x86/lib/memset_64.S"
+
+/*
+ * We need to provide note.GNU-stack section, saying that we want
+ * NOT executable stack. Otherwise the final linking will assume that
+ * the ELF stack should not be restricted at all and set it RWX.
+ */
+.section .note.GNU-stack,"",@progbits
diff --git a/tools/perf/bench/mem-memset.c b/tools/perf/bench/mem-memset.c
new file mode 100644
index 00000000000..e9079185bd7
--- /dev/null
+++ b/tools/perf/bench/mem-memset.c
@@ -0,0 +1,297 @@
+/*
+ * mem-memset.c
+ *
+ * memset: Simple memory set in various ways
+ *
+ * Trivial clone of mem-memcpy.c.
+ */
+
+#include "../perf.h"
+#include "../util/util.h"
+#include "../util/parse-options.h"
+#include "../util/header.h"
+#include "bench.h"
+#include "mem-memset-arch.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#define K 1024
+
+static const char *length_str = "1MB";
+static const char *routine = "default";
+static int iterations = 1;
+static bool use_clock;
+static int clock_fd;
+static bool only_prefault;
+static bool no_prefault;
+
+static const struct option options[] = {
+ OPT_STRING('l', "length", &length_str, "1MB",
+ "Specify length of memory to copy. "
+ "available unit: B, MB, GB (upper and lower)"),
+ OPT_STRING('r', "routine", &routine, "default",
+ "Specify routine to copy"),
+ OPT_INTEGER('i', "iterations", &iterations,
+ "repeat memset() invocation this number of times"),
+ OPT_BOOLEAN('c', "clock", &use_clock,
+ "Use CPU clock for measuring"),
+ OPT_BOOLEAN('o', "only-prefault", &only_prefault,
+ "Show only the result with page faults before memset()"),
+ OPT_BOOLEAN('n', "no-prefault", &no_prefault,
+ "Show only the result without page faults before memset()"),
+ OPT_END()
+};
+
+typedef void *(*memset_t)(void *, int, size_t);
+
+struct routine {
+ const char *name;
+ const char *desc;
+ memset_t fn;
+};
+
+static const struct routine routines[] = {
+ { "default",
+ "Default memset() provided by glibc",
+ memset },
+#ifdef ARCH_X86_64
+
+#define MEMSET_FN(fn, name, desc) { name, desc, fn },
+#include "mem-memset-x86-64-asm-def.h"
+#undef MEMSET_FN
+
+#endif
+
+ { NULL,
+ NULL,
+ NULL }
+};
+
+static const char * const bench_mem_memset_usage[] = {
+ "perf bench mem memset <options>",
+ NULL
+};
+
+static struct perf_event_attr clock_attr = {
+ .type = PERF_TYPE_HARDWARE,
+ .config = PERF_COUNT_HW_CPU_CYCLES
+};
+
+static void init_clock(void)
+{
+ clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0);
+
+ if (clock_fd < 0 && errno == ENOSYS)
+ die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
+ else
+ BUG_ON(clock_fd < 0);
+}
+
+static u64 get_clock(void)
+{
+ int ret;
+ u64 clk;
+
+ ret = read(clock_fd, &clk, sizeof(u64));
+ BUG_ON(ret != sizeof(u64));
+
+ return clk;
+}
+
+static double timeval2double(struct timeval *ts)
+{
+ return (double)ts->tv_sec +
+ (double)ts->tv_usec / (double)1000000;
+}
+
+static void alloc_mem(void **dst, size_t length)
+{
+ *dst = zalloc(length);
+ if (!dst)
+ die("memory allocation failed - maybe length is too large?\n");
+}
+
+static u64 do_memset_clock(memset_t fn, size_t len, bool prefault)
+{
+ u64 clock_start = 0ULL, clock_end = 0ULL;
+ void *dst = NULL;
+ int i;
+
+ alloc_mem(&dst, len);
+
+ if (prefault)
+ fn(dst, -1, len);
+
+ clock_start = get_clock();
+ for (i = 0; i < iterations; ++i)
+ fn(dst, i, len);
+ clock_end = get_clock();
+
+ free(dst);
+ return clock_end - clock_start;
+}
+
+static double do_memset_gettimeofday(memset_t fn, size_t len, bool prefault)
+{
+ struct timeval tv_start, tv_end, tv_diff;
+ void *dst = NULL;
+ int i;
+
+ alloc_mem(&dst, len);
+
+ if (prefault)
+ fn(dst, -1, len);
+
+ BUG_ON(gettimeofday(&tv_start, NULL));
+ for (i = 0; i < iterations; ++i)
+ fn(dst, i, len);
+ BUG_ON(gettimeofday(&tv_end, NULL));
+
+ timersub(&tv_end, &tv_start, &tv_diff);
+
+ free(dst);
+ return (double)((double)len / timeval2double(&tv_diff));
+}
+
+#define pf (no_prefault ? 0 : 1)
+
+#define print_bps(x) do { \
+ if (x < K) \
+ printf(" %14lf B/Sec", x); \
+ else if (x < K * K) \
+ printf(" %14lfd KB/Sec", x / K); \
+ else if (x < K * K * K) \
+ printf(" %14lf MB/Sec", x / K / K); \
+ else \
+ printf(" %14lf GB/Sec", x / K / K / K); \
+ } while (0)
+
+int bench_mem_memset(int argc, const char **argv,
+ const char *prefix __used)
+{
+ int i;
+ size_t len;
+ double result_bps[2];
+ u64 result_clock[2];
+
+ argc = parse_options(argc, argv, options,
+ bench_mem_memset_usage, 0);
+
+ if (use_clock)
+ init_clock();
+
+ len = (size_t)perf_atoll((char *)length_str);
+
+ result_clock[0] = result_clock[1] = 0ULL;
+ result_bps[0] = result_bps[1] = 0.0;
+
+ if ((s64)len <= 0) {
+ fprintf(stderr, "Invalid length:%s\n", length_str);
+ return 1;
+ }
+
+ /* same to without specifying either of prefault and no-prefault */
+ if (only_prefault && no_prefault)
+ only_prefault = no_prefault = false;
+
+ for (i = 0; routines[i].name; i++) {
+ if (!strcmp(routines[i].name, routine))
+ break;
+ }
+ if (!routines[i].name) {
+ printf("Unknown routine:%s\n", routine);
+ printf("Available routines...\n");
+ for (i = 0; routines[i].name; i++) {
+ printf("\t%s ... %s\n",
+ routines[i].name, routines[i].desc);
+ }
+ return 1;
+ }
+
+ if (bench_format == BENCH_FORMAT_DEFAULT)
+ printf("# Copying %s Bytes ...\n\n", length_str);
+
+ if (!only_prefault && !no_prefault) {
+ /* show both of results */
+ if (use_clock) {
+ result_clock[0] =
+ do_memset_clock(routines[i].fn, len, false);
+ result_clock[1] =
+ do_memset_clock(routines[i].fn, len, true);
+ } else {
+ result_bps[0] =
+ do_memset_gettimeofday(routines[i].fn,
+ len, false);
+ result_bps[1] =
+ do_memset_gettimeofday(routines[i].fn,
+ len, true);
+ }
+ } else {
+ if (use_clock) {
+ result_clock[pf] =
+ do_memset_clock(routines[i].fn,
+ len, only_prefault);
+ } else {
+ result_bps[pf] =
+ do_memset_gettimeofday(routines[i].fn,
+ len, only_prefault);
+ }
+ }
+
+ switch (bench_format) {
+ case BENCH_FORMAT_DEFAULT:
+ if (!only_prefault && !no_prefault) {
+ if (use_clock) {
+ printf(" %14lf Clock/Byte\n",
+ (double)result_clock[0]
+ / (double)len);
+ printf(" %14lf Clock/Byte (with prefault)\n ",
+ (double)result_clock[1]
+ / (double)len);
+ } else {
+ print_bps(result_bps[0]);
+ printf("\n");
+ print_bps(result_bps[1]);
+ printf(" (with prefault)\n");
+ }
+ } else {
+ if (use_clock) {
+ printf(" %14lf Clock/Byte",
+ (double)result_clock[pf]
+ / (double)len);
+ } else
+ print_bps(result_bps[pf]);
+
+ printf("%s\n", only_prefault ? " (with prefault)" : "");
+ }
+ break;
+ case BENCH_FORMAT_SIMPLE:
+ if (!only_prefault && !no_prefault) {
+ if (use_clock) {
+ printf("%lf %lf\n",
+ (double)result_clock[0] / (double)len,
+ (double)result_clock[1] / (double)len);
+ } else {
+ printf("%lf %lf\n",
+ result_bps[0], result_bps[1]);
+ }
+ } else {
+ if (use_clock) {
+ printf("%lf\n", (double)result_clock[pf]
+ / (double)len);
+ } else
+ printf("%lf\n", result_bps[pf]);
+ }
+ break;
+ default:
+ /* reaching this means there's some disaster: */
+ die("unknown format: %d\n", bench_format);
+ break;
+ }
+
+ return 0;
+}
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index fcb96269852..b0e74ab2d7a 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -52,6 +52,9 @@ static struct bench_suite mem_suites[] = {
{ "memcpy",
"Simple memory copy in various ways",
bench_mem_memcpy },
+ { "memset",
+ "Simple memory set in various ways",
+ bench_mem_memset },
suite_all,
{ NULL,
NULL,
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 2296c391d0f..12c81483899 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -922,12 +922,12 @@ static const struct option info_options[] = {
OPT_BOOLEAN('t', "threads", &info_threads,
"dump thread list in perf.data"),
OPT_BOOLEAN('m', "map", &info_map,
- "map of lock instances (name:address table)"),
+ "map of lock instances (address:name table)"),
OPT_END()
};
static const char * const lock_usage[] = {
- "perf lock [<options>] {record|trace|report}",
+ "perf lock [<options>] {record|report|script|info}",
NULL
};
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index fb8566181f2..4935c09dd5b 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -58,7 +58,7 @@ static struct {
struct perf_probe_event events[MAX_PROBES];
struct strlist *dellist;
struct line_range line_range;
- const char *target_module;
+ const char *target;
int max_probe_points;
struct strfilter *filter;
} params;
@@ -246,7 +246,7 @@ static const struct option options[] = {
"file", "vmlinux pathname"),
OPT_STRING('s', "source", &symbol_conf.source_prefix,
"directory", "path to kernel source"),
- OPT_STRING('m', "module", &params.target_module,
+ OPT_STRING('m', "module", &params.target,
"modname|path",
"target module name (for online) or path (for offline)"),
#endif
@@ -333,7 +333,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
if (!params.filter)
params.filter = strfilter__new(DEFAULT_FUNC_FILTER,
NULL);
- ret = show_available_funcs(params.target_module,
+ ret = show_available_funcs(params.target,
params.filter);
strfilter__delete(params.filter);
if (ret < 0)
@@ -354,7 +354,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
usage_with_options(probe_usage, options);
}
- ret = show_line_range(&params.line_range, params.target_module);
+ ret = show_line_range(&params.line_range, params.target);
if (ret < 0)
pr_err(" Error: Failed to show lines. (%d)\n", ret);
return ret;
@@ -371,7 +371,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
ret = show_available_vars(params.events, params.nevents,
params.max_probe_points,
- params.target_module,
+ params.target,
params.filter,
params.show_ext_vars);
strfilter__delete(params.filter);
@@ -393,7 +393,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
if (params.nevents) {
ret = add_perf_probe_events(params.events, params.nevents,
params.max_probe_points,
- params.target_module,
+ params.target,
params.force_add);
if (ret < 0) {
pr_err(" Error: Failed to add events. (%d)\n", ret);
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 227b6ae9978..be4e1eee782 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -44,6 +44,7 @@ struct perf_record {
struct perf_evlist *evlist;
struct perf_session *session;
const char *progname;
+ const char *uid_str;
int output;
unsigned int page_size;
int realtime_prio;
@@ -208,7 +209,7 @@ fallback_missing_features:
if (opts->exclude_guest_missing)
attr->exclude_guest = attr->exclude_host = 0;
retry_sample_id:
- attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0;
+ attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1;
try_again:
if (perf_evsel__open(pos, evlist->cpus, evlist->threads,
opts->group, group_fd) < 0) {
@@ -227,11 +228,11 @@ try_again:
"guest or host samples.\n");
opts->exclude_guest_missing = true;
goto fallback_missing_features;
- } else if (opts->sample_id_all_avail) {
+ } else if (!opts->sample_id_all_missing) {
/*
* Old kernel, no attr->sample_id_type_all field
*/
- opts->sample_id_all_avail = false;
+ opts->sample_id_all_missing = true;
if (!opts->sample_time && !opts->raw_samples && !time_needed)
attr->sample_type &= ~PERF_SAMPLE_TIME;
@@ -396,7 +397,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
{
struct stat st;
int flags;
- int err, output;
+ int err, output, feat;
unsigned long waking = 0;
const bool forks = argc > 0;
struct machine *machine;
@@ -463,8 +464,17 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
rec->session = session;
- if (!rec->no_buildid)
- perf_header__set_feat(&session->header, HEADER_BUILD_ID);
+ for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++)
+ perf_header__set_feat(&session->header, feat);
+
+ if (rec->no_buildid)
+ perf_header__clear_feat(&session->header, HEADER_BUILD_ID);
+
+ if (!have_tracepoints(&evsel_list->entries))
+ perf_header__clear_feat(&session->header, HEADER_TRACE_INFO);
+
+ if (!rec->opts.branch_stack)
+ perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
if (!rec->file_new) {
err = perf_session__read_header(session, output);
@@ -472,22 +482,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
goto out_delete_session;
}
- 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);
-
if (forks) {
err = perf_evlist__prepare_workload(evsel_list, opts, argv);
if (err < 0) {
@@ -647,6 +641,90 @@ out_delete_session:
return err;
}
+#define BRANCH_OPT(n, m) \
+ { .name = n, .mode = (m) }
+
+#define BRANCH_END { .name = NULL }
+
+struct branch_mode {
+ const char *name;
+ int mode;
+};
+
+static const struct branch_mode branch_modes[] = {
+ BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER),
+ BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL),
+ BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV),
+ BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY),
+ BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL),
+ BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN),
+ BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL),
+ BRANCH_END
+};
+
+static int
+parse_branch_stack(const struct option *opt, const char *str, int unset)
+{
+#define ONLY_PLM \
+ (PERF_SAMPLE_BRANCH_USER |\
+ PERF_SAMPLE_BRANCH_KERNEL |\
+ PERF_SAMPLE_BRANCH_HV)
+
+ uint64_t *mode = (uint64_t *)opt->value;
+ const struct branch_mode *br;
+ char *s, *os = NULL, *p;
+ int ret = -1;
+
+ if (unset)
+ return 0;
+
+ /*
+ * cannot set it twice, -b + --branch-filter for instance
+ */
+ if (*mode)
+ return -1;
+
+ /* str may be NULL in case no arg is passed to -b */
+ if (str) {
+ /* because str is read-only */
+ s = os = strdup(str);
+ if (!s)
+ return -1;
+
+ for (;;) {
+ p = strchr(s, ',');
+ if (p)
+ *p = '\0';
+
+ for (br = branch_modes; br->name; br++) {
+ if (!strcasecmp(s, br->name))
+ break;
+ }
+ if (!br->name) {
+ ui__warning("unknown branch filter %s,"
+ " check man page\n", s);
+ goto error;
+ }
+
+ *mode |= br->mode;
+
+ if (!p)
+ break;
+
+ s = p + 1;
+ }
+ }
+ ret = 0;
+
+ /* default to any branch */
+ if ((*mode & ~ONLY_PLM) == 0) {
+ *mode = PERF_SAMPLE_BRANCH_ANY;
+ }
+error:
+ free(os);
+ return ret;
+}
+
static const char * const record_usage[] = {
"perf record [<options>] [<command>]",
"perf record [<options>] -- <command> [<options>]",
@@ -665,13 +743,10 @@ static const char * const record_usage[] = {
*/
static struct perf_record record = {
.opts = {
- .target_pid = -1,
- .target_tid = -1,
.mmap_pages = UINT_MAX,
.user_freq = UINT_MAX,
.user_interval = ULLONG_MAX,
.freq = 1000,
- .sample_id_all_avail = true,
},
.write_mode = WRITE_FORCE,
.file_new = true,
@@ -690,9 +765,9 @@ const struct option record_options[] = {
parse_events_option),
OPT_CALLBACK(0, "filter", &record.evlist, "filter",
"event filter", parse_filter),
- OPT_INTEGER('p', "pid", &record.opts.target_pid,
+ OPT_STRING('p', "pid", &record.opts.target_pid, "pid",
"record events on existing process id"),
- OPT_INTEGER('t', "tid", &record.opts.target_tid,
+ OPT_STRING('t', "tid", &record.opts.target_tid, "tid",
"record events on existing thread id"),
OPT_INTEGER('r', "realtime", &record.realtime_prio,
"collect data with this RT SCHED_FIFO priority"),
@@ -738,6 +813,15 @@ const struct option record_options[] = {
OPT_CALLBACK('G', "cgroup", &record.evlist, "name",
"monitor event in cgroup name only",
parse_cgroups),
+ OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"),
+
+ OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack,
+ "branch any", "sample any taken branches",
+ parse_branch_stack),
+
+ OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack,
+ "branch filter mask", "branch stack filter modes",
+ parse_branch_stack),
OPT_END()
};
@@ -758,8 +842,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
argc = parse_options(argc, argv, record_options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
- if (!argc && rec->opts.target_pid == -1 && rec->opts.target_tid == -1 &&
- !rec->opts.system_wide && !rec->opts.cpu_list)
+ if (!argc && !rec->opts.target_pid && !rec->opts.target_tid &&
+ !rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str)
usage_with_options(record_usage, record_options);
if (rec->force && rec->append_file) {
@@ -799,11 +883,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
goto out_symbol_exit;
}
- if (rec->opts.target_pid != -1)
+ rec->opts.uid = parse_target_uid(rec->uid_str, rec->opts.target_tid,
+ rec->opts.target_pid);
+ if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1)
+ goto out_free_fd;
+
+ if (rec->opts.target_pid)
rec->opts.target_tid = rec->opts.target_pid;
if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid,
- rec->opts.target_tid, rec->opts.cpu_list) < 0)
+ rec->opts.target_tid, rec->opts.uid,
+ rec->opts.cpu_list) < 0)
usage_with_options(record_usage, record_options);
list_for_each_entry(pos, &evsel_list->entries, node) {
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 25d34d483e4..8e91c6eba18 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -53,6 +53,82 @@ struct perf_report {
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
};
+static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
+ struct addr_location *al,
+ struct perf_sample *sample,
+ struct perf_evsel *evsel,
+ struct machine *machine)
+{
+ struct perf_report *rep = container_of(tool, struct perf_report, tool);
+ struct symbol *parent = NULL;
+ int err = 0;
+ unsigned i;
+ struct hist_entry *he;
+ struct branch_info *bi, *bx;
+
+ if ((sort__has_parent || symbol_conf.use_callchain)
+ && sample->callchain) {
+ err = machine__resolve_callchain(machine, evsel, al->thread,
+ sample->callchain, &parent);
+ if (err)
+ return err;
+ }
+
+ bi = machine__resolve_bstack(machine, al->thread,
+ sample->branch_stack);
+ if (!bi)
+ return -ENOMEM;
+
+ for (i = 0; i < sample->branch_stack->nr; i++) {
+ if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
+ continue;
+ /*
+ * The report shows the percentage of total branches captured
+ * and not events sampled. Thus we use a pseudo period of 1.
+ */
+ he = __hists__add_branch_entry(&evsel->hists, al, parent,
+ &bi[i], 1);
+ if (he) {
+ struct annotation *notes;
+ err = -ENOMEM;
+ bx = he->branch_info;
+ if (bx->from.sym && use_browser > 0) {
+ notes = symbol__annotation(bx->from.sym);
+ if (!notes->src
+ && symbol__alloc_hist(bx->from.sym) < 0)
+ goto out;
+
+ err = symbol__inc_addr_samples(bx->from.sym,
+ bx->from.map,
+ evsel->idx,
+ bx->from.al_addr);
+ if (err)
+ goto out;
+ }
+
+ if (bx->to.sym && use_browser > 0) {
+ notes = symbol__annotation(bx->to.sym);
+ if (!notes->src
+ && symbol__alloc_hist(bx->to.sym) < 0)
+ goto out;
+
+ err = symbol__inc_addr_samples(bx->to.sym,
+ bx->to.map,
+ evsel->idx,
+ bx->to.al_addr);
+ if (err)
+ goto out;
+ }
+ evsel->hists.stats.total_period += 1;
+ hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+ err = 0;
+ } else
+ return -ENOMEM;
+ }
+out:
+ return err;
+}
+
static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
struct addr_location *al,
struct perf_sample *sample,
@@ -126,14 +202,21 @@ static int process_sample_event(struct perf_tool *tool,
if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
return 0;
- if (al.map != NULL)
- al.map->dso->hit = 1;
+ if (sort__branch_mode == 1) {
+ if (perf_report__add_branch_hist_entry(tool, &al, sample,
+ evsel, machine)) {
+ pr_debug("problem adding lbr entry, skipping event\n");
+ return -1;
+ }
+ } else {
+ if (al.map != NULL)
+ al.map->dso->hit = 1;
- if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) {
- pr_debug("problem incrementing symbol period, skipping event\n");
- return -1;
+ if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) {
+ pr_debug("problem incrementing symbol period, skipping event\n");
+ return -1;
+ }
}
-
return 0;
}
@@ -188,6 +271,15 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
}
}
+ if (sort__branch_mode == 1) {
+ if (!(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) {
+ fprintf(stderr, "selected -b but no branch data."
+ " Did you call perf record without"
+ " -b?\n");
+ return -1;
+ }
+ }
+
return 0;
}
@@ -246,7 +338,7 @@ static int __cmd_report(struct perf_report *rep)
{
int ret = -EINVAL;
u64 nr_samples;
- struct perf_session *session;
+ struct perf_session *session = rep->session;
struct perf_evsel *pos;
struct map *kernel_map;
struct kmap *kernel_kmap;
@@ -254,13 +346,6 @@ static int __cmd_report(struct perf_report *rep)
signal(SIGINT, sig_handler);
- session = perf_session__new(rep->input_name, O_RDONLY,
- rep->force, false, &rep->tool);
- if (session == NULL)
- return -ENOMEM;
-
- rep->session = session;
-
if (rep->cpu_list) {
ret = perf_session__cpu_bitmap(session, rep->cpu_list,
rep->cpu_bitmap);
@@ -427,9 +512,19 @@ setup:
return 0;
}
+static int
+parse_branch_mode(const struct option *opt __used, const char *str __used, int unset)
+{
+ sort__branch_mode = !unset;
+ return 0;
+}
+
int cmd_report(int argc, const char **argv, const char *prefix __used)
{
+ struct perf_session *session;
struct stat st;
+ bool has_br_stack = false;
+ int ret = -1;
char callchain_default_opt[] = "fractal,0.5,callee";
const char * const report_usage[] = {
"perf report [<options>]",
@@ -477,7 +572,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
OPT_BOOLEAN(0, "stdio", &report.use_stdio,
"Use the stdio interface"),
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
- "sort by key(s): pid, comm, dso, symbol, parent"),
+ "sort by key(s): pid, comm, dso, symbol, parent, dso_to,"
+ " dso_from, symbol_to, symbol_from, mispredict"),
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
"Show sample percentage for different cpu modes"),
OPT_STRING('p', "parent", &parent_pattern, "regex",
@@ -517,6 +613,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
"Specify disassembler style (e.g. -M intel for intel syntax)"),
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
"Show a column with the sum of periods"),
+ OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "",
+ "use branch records for histogram filling", parse_branch_mode),
OPT_END()
};
@@ -536,11 +634,36 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
else
report.input_name = "perf.data";
}
+ session = perf_session__new(report.input_name, O_RDONLY,
+ report.force, false, &report.tool);
+ if (session == NULL)
+ return -ENOMEM;
- if (strcmp(report.input_name, "-") != 0)
+ report.session = session;
+
+ has_br_stack = perf_header__has_feat(&session->header,
+ HEADER_BRANCH_STACK);
+
+ if (sort__branch_mode == -1 && has_br_stack)
+ sort__branch_mode = 1;
+
+ /* sort__branch_mode could be 0 if --no-branch-stack */
+ if (sort__branch_mode == 1) {
+ /*
+ * if no sort_order is provided, then specify
+ * branch-mode specific order
+ */
+ if (sort_order == default_sort_order)
+ sort_order = "comm,dso_from,symbol_from,"
+ "dso_to,symbol_to";
+
+ }
+
+ if (strcmp(report.input_name, "-") != 0) {
setup_browser(true);
- else
+ } else {
use_browser = 0;
+ }
/*
* Only in the newt browser we are doing integrated annotation,
@@ -568,13 +691,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
}
if (symbol__init() < 0)
- return -1;
+ goto error;
setup_sorting(report_usage, options);
if (parent_pattern != default_parent_pattern) {
if (sort_dimension__add("parent") < 0)
- return -1;
+ goto error;
/*
* Only show the parent fields if we explicitly
@@ -592,9 +715,20 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
if (argc)
usage_with_options(report_usage, options);
- sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
- sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
- return __cmd_report(&report);
+ if (sort__branch_mode == 1) {
+ sort_entry__setup_elide(&sort_dso_from, symbol_conf.dso_from_list, "dso_from", stdout);
+ sort_entry__setup_elide(&sort_dso_to, symbol_conf.dso_to_list, "dso_to", stdout);
+ sort_entry__setup_elide(&sort_sym_from, symbol_conf.sym_from_list, "sym_from", stdout);
+ sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", stdout);
+ } else {
+ sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
+ sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
+ }
+
+ ret = __cmd_report(&report);
+error:
+ perf_session__delete(session);
+ return ret;
}
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index bb68ddf257b..d4ce733b9eb 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -40,6 +40,7 @@ enum perf_output_field {
PERF_OUTPUT_SYM = 1U << 8,
PERF_OUTPUT_DSO = 1U << 9,
PERF_OUTPUT_ADDR = 1U << 10,
+ PERF_OUTPUT_SYMOFFSET = 1U << 11,
};
struct output_option {
@@ -57,6 +58,7 @@ struct output_option {
{.str = "sym", .field = PERF_OUTPUT_SYM},
{.str = "dso", .field = PERF_OUTPUT_DSO},
{.str = "addr", .field = PERF_OUTPUT_ADDR},
+ {.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET},
};
/* default set to maintain compatibility with current format */
@@ -193,6 +195,11 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
"to symbols.\n");
return -EINVAL;
}
+ if (PRINT_FIELD(SYMOFFSET) && !PRINT_FIELD(SYM)) {
+ pr_err("Display of offsets requested but symbol is not"
+ "selected.\n");
+ return -EINVAL;
+ }
if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) {
pr_err("Display of DSO requested but neither sample IP nor "
"sample address\nis selected. Hence, no addresses to convert "
@@ -300,10 +307,17 @@ static void print_sample_start(struct perf_sample *sample,
} else
evname = __event_name(attr->type, attr->config);
- printf("%s: ", evname ? evname : "(unknown)");
+ printf("%s: ", evname ? evname : "[unknown]");
}
}
+static bool is_bts_event(struct perf_event_attr *attr)
+{
+ return ((attr->type == PERF_TYPE_HARDWARE) &&
+ (attr->config & PERF_COUNT_HW_BRANCH_INSTRUCTIONS) &&
+ (attr->sample_period == 1));
+}
+
static bool sample_addr_correlates_sym(struct perf_event_attr *attr)
{
if ((attr->type == PERF_TYPE_SOFTWARE) &&
@@ -312,6 +326,9 @@ static bool sample_addr_correlates_sym(struct perf_event_attr *attr)
(attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)))
return true;
+ if (is_bts_event(attr))
+ return true;
+
return false;
}
@@ -323,7 +340,6 @@ static void print_sample_addr(union perf_event *event,
{
struct addr_location al;
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
- const char *symname, *dsoname;
printf("%16" PRIx64, sample->addr);
@@ -343,22 +359,46 @@ static void print_sample_addr(union perf_event *event,
al.sym = map__find_symbol(al.map, al.addr, NULL);
if (PRINT_FIELD(SYM)) {
- if (al.sym && al.sym->name)
- symname = al.sym->name;
+ printf(" ");
+ if (PRINT_FIELD(SYMOFFSET))
+ symbol__fprintf_symname_offs(al.sym, &al, stdout);
else
- symname = "";
-
- printf(" %16s", symname);
+ symbol__fprintf_symname(al.sym, stdout);
}
if (PRINT_FIELD(DSO)) {
- if (al.map && al.map->dso && al.map->dso->name)
- dsoname = al.map->dso->name;
- else
- dsoname = "";
+ printf(" (");
+ map__fprintf_dsoname(al.map, stdout);
+ printf(")");
+ }
+}
- printf(" (%s)", dsoname);
+static void print_sample_bts(union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_evsel *evsel,
+ struct machine *machine,
+ struct thread *thread)
+{
+ struct perf_event_attr *attr = &evsel->attr;
+
+ /* print branch_from information */
+ if (PRINT_FIELD(IP)) {
+ if (!symbol_conf.use_callchain)
+ printf(" ");
+ else
+ printf("\n");
+ perf_event__print_ip(event, sample, machine, evsel,
+ PRINT_FIELD(SYM), PRINT_FIELD(DSO),
+ PRINT_FIELD(SYMOFFSET));
}
+
+ printf(" => ");
+
+ /* print branch_to information */
+ if (PRINT_FIELD(ADDR))
+ print_sample_addr(event, sample, machine, thread, attr);
+
+ printf("\n");
}
static void process_event(union perf_event *event __unused,
@@ -374,6 +414,11 @@ static void process_event(union perf_event *event __unused,
print_sample_start(sample, thread, attr);
+ if (is_bts_event(attr)) {
+ print_sample_bts(event, sample, evsel, machine, thread);
+ return;
+ }
+
if (PRINT_FIELD(TRACE))
print_trace_event(sample->cpu, sample->raw_data,
sample->raw_size);
@@ -387,7 +432,8 @@ static void process_event(union perf_event *event __unused,
else
printf("\n");
perf_event__print_ip(event, sample, machine, evsel,
- PRINT_FIELD(SYM), PRINT_FIELD(DSO));
+ PRINT_FIELD(SYM), PRINT_FIELD(DSO),
+ PRINT_FIELD(SYMOFFSET));
}
printf("\n");
@@ -1097,7 +1143,10 @@ static const struct option options[] = {
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
OPT_CALLBACK('f', "fields", NULL, "str",
- "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",
+ "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,symoff",
parse_output_fields),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
@@ -1106,6 +1155,9 @@ static const struct option options[] = {
"only display events for these comms"),
OPT_BOOLEAN('I', "show-info", &show_full_info,
"display extended information from perf.data file"),
+ OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
+ "Show the path of [kernel.kallsyms]"),
+
OPT_END()
};
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index f5d2a63eba6..ea40e4e8b22 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -182,8 +182,8 @@ static int run_count = 1;
static bool no_inherit = false;
static bool scale = true;
static bool no_aggr = false;
-static pid_t target_pid = -1;
-static pid_t target_tid = -1;
+static const char *target_pid;
+static const char *target_tid;
static pid_t child_pid = -1;
static bool null_run = false;
static int detailed_run = 0;
@@ -296,7 +296,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel,
if (system_wide)
return perf_evsel__open_per_cpu(evsel, evsel_list->cpus,
group, group_fd);
- if (target_pid == -1 && target_tid == -1) {
+ if (!target_pid && !target_tid) {
attr->disabled = 1;
attr->enable_on_exec = 1;
}
@@ -446,7 +446,7 @@ static int run_perf_stat(int argc __used, const char **argv)
exit(-1);
}
- if (target_tid == -1 && target_pid == -1 && !system_wide)
+ if (!target_tid && !target_pid && !system_wide)
evsel_list->threads->map[0] = child_pid;
/*
@@ -576,6 +576,8 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK))
fprintf(output, " # %8.3f CPUs utilized ",
avg / avg_stats(&walltime_nsecs_stats));
+ else
+ fprintf(output, " ");
}
/* used for get_ratio_color() */
@@ -844,12 +846,18 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
fprintf(output, " # %8.3f GHz ", ratio);
} else if (runtime_nsecs_stats[cpu].n != 0) {
+ char unit = 'M';
+
total = avg_stats(&runtime_nsecs_stats[cpu]);
if (total)
ratio = 1000.0 * avg / total;
+ if (ratio < 0.001) {
+ ratio *= 1000;
+ unit = 'K';
+ }
- fprintf(output, " # %8.3f M/sec ", ratio);
+ fprintf(output, " # %8.3f %c/sec ", ratio, unit);
} else {
fprintf(output, " ");
}
@@ -960,14 +968,14 @@ static void print_stat(int argc, const char **argv)
if (!csv_output) {
fprintf(output, "\n");
fprintf(output, " Performance counter stats for ");
- if(target_pid == -1 && target_tid == -1) {
+ if (!target_pid && !target_tid) {
fprintf(output, "\'%s", argv[0]);
for (i = 1; i < argc; i++)
fprintf(output, " %s", argv[i]);
- } else if (target_pid != -1)
- fprintf(output, "process id \'%d", target_pid);
+ } else if (target_pid)
+ fprintf(output, "process id \'%s", target_pid);
else
- fprintf(output, "thread id \'%d", target_tid);
+ fprintf(output, "thread id \'%s", target_tid);
fprintf(output, "\'");
if (run_count > 1)
@@ -1041,10 +1049,10 @@ static const struct option options[] = {
"event filter", parse_filter),
OPT_BOOLEAN('i', "no-inherit", &no_inherit,
"child tasks do not inherit counters"),
- OPT_INTEGER('p', "pid", &target_pid,
- "stat events on existing process id"),
- OPT_INTEGER('t', "tid", &target_tid,
- "stat events on existing thread id"),
+ OPT_STRING('p', "pid", &target_pid, "pid",
+ "stat events on existing process id"),
+ OPT_STRING('t', "tid", &target_tid, "tid",
+ "stat events on existing thread id"),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
OPT_BOOLEAN('g', "group", &group,
@@ -1182,7 +1190,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
} else if (big_num_opt == 0) /* User passed --no-big-num */
big_num = false;
- if (!argc && target_pid == -1 && target_tid == -1)
+ if (!argc && !target_pid && !target_tid)
usage_with_options(stat_usage, options);
if (run_count <= 0)
usage_with_options(stat_usage, options);
@@ -1198,10 +1206,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
if (add_default_attributes())
goto out;
- if (target_pid != -1)
+ if (target_pid)
target_tid = target_pid;
- evsel_list->threads = thread_map__new(target_pid, target_tid);
+ evsel_list->threads = thread_map__new_str(target_pid,
+ target_tid, UINT_MAX);
if (evsel_list->threads == NULL) {
pr_err("Problems finding threads of monitor\n");
usage_with_options(stat_usage, options);
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index 3854e869dce..3e087ce8daa 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -15,6 +15,8 @@
#include "util/thread_map.h"
#include "../../include/linux/hw_breakpoint.h"
+#include <sys/mman.h>
+
static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym)
{
bool *visited = symbol__priv(sym);
@@ -276,7 +278,7 @@ static int test__open_syscall_event(void)
return -1;
}
- threads = thread_map__new(-1, getpid());
+ threads = thread_map__new(-1, getpid(), UINT_MAX);
if (threads == NULL) {
pr_debug("thread_map__new\n");
return -1;
@@ -342,7 +344,7 @@ static int test__open_syscall_event_on_all_cpus(void)
return -1;
}
- threads = thread_map__new(-1, getpid());
+ threads = thread_map__new(-1, getpid(), UINT_MAX);
if (threads == NULL) {
pr_debug("thread_map__new\n");
return -1;
@@ -490,7 +492,7 @@ static int test__basic_mmap(void)
expected_nr_events[i] = random() % 257;
}
- threads = thread_map__new(-1, getpid());
+ threads = thread_map__new(-1, getpid(), UINT_MAX);
if (threads == NULL) {
pr_debug("thread_map__new\n");
return -1;
@@ -1008,12 +1010,9 @@ realloc:
static int test__PERF_RECORD(void)
{
struct perf_record_opts opts = {
- .target_pid = -1,
- .target_tid = -1,
.no_delay = true,
.freq = 10,
.mmap_pages = 256,
- .sample_id_all_avail = true,
};
cpu_set_t *cpu_mask = NULL;
size_t cpu_mask_size = 0;
@@ -1054,7 +1053,7 @@ static int test__PERF_RECORD(void)
* we're monitoring, the one forked there.
*/
err = perf_evlist__create_maps(evlist, opts.target_pid,
- opts.target_tid, opts.cpu_list);
+ opts.target_tid, UINT_MAX, opts.cpu_list);
if (err < 0) {
pr_debug("Not enough memory to create thread/cpu maps\n");
goto out_delete_evlist;
@@ -1296,6 +1295,173 @@ out:
return (err < 0 || errs > 0) ? -1 : 0;
}
+
+#if defined(__x86_64__) || defined(__i386__)
+
+#define barrier() asm volatile("" ::: "memory")
+
+static u64 rdpmc(unsigned int counter)
+{
+ unsigned int low, high;
+
+ asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter));
+
+ return low | ((u64)high) << 32;
+}
+
+static u64 rdtsc(void)
+{
+ unsigned int low, high;
+
+ asm volatile("rdtsc" : "=a" (low), "=d" (high));
+
+ return low | ((u64)high) << 32;
+}
+
+static u64 mmap_read_self(void *addr)
+{
+ struct perf_event_mmap_page *pc = addr;
+ u32 seq, idx, time_mult = 0, time_shift = 0;
+ u64 count, cyc = 0, time_offset = 0, enabled, running, delta;
+
+ do {
+ seq = pc->lock;
+ barrier();
+
+ enabled = pc->time_enabled;
+ running = pc->time_running;
+
+ if (enabled != running) {
+ cyc = rdtsc();
+ time_mult = pc->time_mult;
+ time_shift = pc->time_shift;
+ time_offset = pc->time_offset;
+ }
+
+ idx = pc->index;
+ count = pc->offset;
+ if (idx)
+ count += rdpmc(idx - 1);
+
+ barrier();
+ } while (pc->lock != seq);
+
+ if (enabled != running) {
+ u64 quot, rem;
+
+ quot = (cyc >> time_shift);
+ rem = cyc & ((1 << time_shift) - 1);
+ delta = time_offset + quot * time_mult +
+ ((rem * time_mult) >> time_shift);
+
+ enabled += delta;
+ if (idx)
+ running += delta;
+
+ quot = count / running;
+ rem = count % running;
+ count = quot * enabled + (rem * enabled) / running;
+ }
+
+ return count;
+}
+
+/*
+ * If the RDPMC instruction faults then signal this back to the test parent task:
+ */
+static void segfault_handler(int sig __used, siginfo_t *info __used, void *uc __used)
+{
+ exit(-1);
+}
+
+static int __test__rdpmc(void)
+{
+ long page_size = sysconf(_SC_PAGE_SIZE);
+ volatile int tmp = 0;
+ u64 i, loops = 1000;
+ int n;
+ int fd;
+ void *addr;
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_HARDWARE,
+ .config = PERF_COUNT_HW_INSTRUCTIONS,
+ .exclude_kernel = 1,
+ };
+ u64 delta_sum = 0;
+ struct sigaction sa;
+
+ sigfillset(&sa.sa_mask);
+ sa.sa_sigaction = segfault_handler;
+ sigaction(SIGSEGV, &sa, NULL);
+
+ fprintf(stderr, "\n\n");
+
+ fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+ if (fd < 0) {
+ die("Error: sys_perf_event_open() syscall returned "
+ "with %d (%s)\n", fd, strerror(errno));
+ }
+
+ addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (addr == (void *)(-1)) {
+ die("Error: mmap() syscall returned "
+ "with (%s)\n", strerror(errno));
+ }
+
+ for (n = 0; n < 6; n++) {
+ u64 stamp, now, delta;
+
+ stamp = mmap_read_self(addr);
+
+ for (i = 0; i < loops; i++)
+ tmp++;
+
+ now = mmap_read_self(addr);
+ loops *= 10;
+
+ delta = now - stamp;
+ fprintf(stderr, "%14d: %14Lu\n", n, (long long)delta);
+
+ delta_sum += delta;
+ }
+
+ munmap(addr, page_size);
+ close(fd);
+
+ fprintf(stderr, " ");
+
+ if (!delta_sum)
+ return -1;
+
+ return 0;
+}
+
+static int test__rdpmc(void)
+{
+ int status = 0;
+ int wret = 0;
+ int ret;
+ int pid;
+
+ pid = fork();
+ if (pid < 0)
+ return -1;
+
+ if (!pid) {
+ ret = __test__rdpmc();
+
+ exit(ret);
+ }
+
+ wret = waitpid(pid, &status, 0);
+ if (wret < 0 || status)
+ return -1;
+
+ return 0;
+}
+
+#endif
+
static struct test {
const char *desc;
int (*func)(void);
@@ -1320,6 +1486,12 @@ static struct test {
.desc = "parse events tests",
.func = test__parse_events,
},
+#if defined(__x86_64__) || defined(__i386__)
+ {
+ .desc = "x86 rdpmc test",
+ .func = test__rdpmc,
+ },
+#endif
{
.desc = "Validate PERF_RECORD_* events & perf_sample fields",
.func = test__PERF_RECORD,
@@ -1412,7 +1584,5 @@ int cmd_test(int argc, const char **argv, const char *prefix __used)
if (symbol__init() < 0)
return -1;
- setup_pager();
-
return __cmd_test(argc, argv);
}
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index ecff31257eb..e3c63aef8ef 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -64,7 +64,6 @@
#include <linux/unistd.h>
#include <linux/types.h>
-
void get_term_dimensions(struct winsize *ws)
{
char *s = getenv("LINES");
@@ -544,10 +543,20 @@ static void perf_top__sort_new_samples(void *arg)
static void *display_thread_tui(void *arg)
{
+ struct perf_evsel *pos;
struct perf_top *top = arg;
const char *help = "For a higher level overview, try: perf top --sort comm,dso";
perf_top__sort_new_samples(top);
+
+ /*
+ * Initialize the uid_filter_str, in the future the TUI will allow
+ * Zooming in/out UIDs. For now juse use whatever the user passed
+ * via --uid.
+ */
+ list_for_each_entry(pos, &top->evlist->entries, node)
+ pos->hists.uid_filter_str = top->uid_str;
+
perf_evlist__tui_browse_hists(top->evlist, help,
perf_top__sort_new_samples,
top, top->delay_secs);
@@ -668,6 +677,12 @@ static void perf_event__process_sample(struct perf_tool *tool,
return;
}
+ if (!machine) {
+ pr_err("%u unprocessable samples recorded.",
+ top->session->hists.stats.nr_unprocessable_samples++);
+ return;
+ }
+
if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)
top->exact_samples++;
@@ -861,7 +876,7 @@ fallback_missing_features:
if (top->exclude_guest_missing)
attr->exclude_guest = attr->exclude_host = 0;
retry_sample_id:
- attr->sample_id_all = top->sample_id_all_avail ? 1 : 0;
+ attr->sample_id_all = top->sample_id_all_missing ? 0 : 1;
try_again:
if (perf_evsel__open(counter, top->evlist->cpus,
top->evlist->threads, top->group,
@@ -878,11 +893,11 @@ try_again:
"guest or host samples.\n");
top->exclude_guest_missing = true;
goto fallback_missing_features;
- } else if (top->sample_id_all_avail) {
+ } else if (!top->sample_id_all_missing) {
/*
* Old kernel, no attr->sample_id_type_all field
*/
- top->sample_id_all_avail = false;
+ top->sample_id_all_missing = true;
goto retry_sample_id;
}
}
@@ -967,7 +982,7 @@ static int __cmd_top(struct perf_top *top)
if (ret)
goto out_delete;
- if (top->target_tid != -1)
+ if (top->target_tid || top->uid != UINT_MAX)
perf_event__synthesize_thread_map(&top->tool, top->evlist->threads,
perf_event__process,
&top->session->host_machine);
@@ -1105,10 +1120,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
struct perf_top top = {
.count_filter = 5,
.delay_secs = 2,
- .target_pid = -1,
- .target_tid = -1,
+ .uid = UINT_MAX,
.freq = 1000, /* 1 KHz */
- .sample_id_all_avail = true,
.mmap_pages = 128,
.sym_pcnt_filter = 5,
};
@@ -1119,9 +1132,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
parse_events_option),
OPT_INTEGER('c', "count", &top.default_interval,
"event period to sample"),
- OPT_INTEGER('p', "pid", &top.target_pid,
+ OPT_STRING('p', "pid", &top.target_pid, "pid",
"profile events on existing process id"),
- OPT_INTEGER('t', "tid", &top.target_tid,
+ OPT_STRING('t', "tid", &top.target_tid, "tid",
"profile events on existing thread id"),
OPT_BOOLEAN('a', "all-cpus", &top.system_wide,
"system-wide collection from all CPUs"),
@@ -1180,6 +1193,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
"Display raw encoding of assembly instructions (default)"),
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
"Specify disassembler style (e.g. -M intel for intel syntax)"),
+ OPT_STRING('u', "uid", &top.uid_str, "user", "user to profile"),
OPT_END()
};
@@ -1205,18 +1219,22 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
setup_browser(false);
+ top.uid = parse_target_uid(top.uid_str, top.target_tid, top.target_pid);
+ if (top.uid_str != NULL && top.uid == UINT_MAX - 1)
+ goto out_delete_evlist;
+
/* CPU and PID are mutually exclusive */
- if (top.target_tid > 0 && top.cpu_list) {
+ if (top.target_tid && top.cpu_list) {
printf("WARNING: PID switch overriding CPU\n");
sleep(1);
top.cpu_list = NULL;
}
- if (top.target_pid != -1)
+ if (top.target_pid)
top.target_tid = top.target_pid;
if (perf_evlist__create_maps(top.evlist, top.target_pid,
- top.target_tid, top.cpu_list) < 0)
+ top.target_tid, top.uid, top.cpu_list) < 0)
usage_with_options(top_usage, options);
if (!top.evlist->nr_entries &&
@@ -1280,6 +1298,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
status = __cmd_top(&top);
+out_delete_evlist:
perf_evlist__delete(top.evlist);
return status;
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index 3afa39ac1d4..89e3355ab17 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -173,7 +173,6 @@ sys_perf_event_open(struct perf_event_attr *attr,
pid_t pid, int cpu, int group_fd,
unsigned long flags)
{
- attr->size = sizeof(*attr);
return syscall(__NR_perf_event_open, attr, pid, cpu,
group_fd, flags);
}
@@ -186,14 +185,32 @@ struct ip_callchain {
u64 ips[0];
};
+struct branch_flags {
+ u64 mispred:1;
+ u64 predicted:1;
+ u64 reserved:62;
+};
+
+struct branch_entry {
+ u64 from;
+ u64 to;
+ struct branch_flags flags;
+};
+
+struct branch_stack {
+ u64 nr;
+ struct branch_entry entries[0];
+};
+
extern bool perf_host, perf_guest;
extern const char perf_version_string[];
void pthread__unblock_sigwinch(void);
struct perf_record_opts {
- pid_t target_pid;
- pid_t target_tid;
+ const char *target_pid;
+ const char *target_tid;
+ uid_t uid;
bool call_graph;
bool group;
bool inherit_stat;
@@ -204,13 +221,14 @@ struct perf_record_opts {
bool raw_samples;
bool sample_address;
bool sample_time;
- bool sample_id_all_avail;
+ bool sample_id_all_missing;
bool exclude_guest_missing;
bool system_wide;
bool period;
unsigned int freq;
unsigned int mmap_pages;
unsigned int user_freq;
+ int branch_stack;
u64 default_interval;
u64 user_interval;
const char *cpu_list;
diff --git a/tools/perf/python/twatch.py b/tools/perf/python/twatch.py
index df638c438a9..b11cca58423 100755
--- a/tools/perf/python/twatch.py
+++ b/tools/perf/python/twatch.py
@@ -19,7 +19,7 @@ def main():
cpus = perf.cpu_map()
threads = perf.thread_map()
evsel = perf.evsel(task = 1, comm = 1, mmap = 0,
- wakeup_events = 1, sample_period = 1,
+ wakeup_events = 1, watermark = 1,
sample_id_all = 1,
sample_type = perf.SAMPLE_PERIOD | perf.SAMPLE_TID | perf.SAMPLE_CPU | perf.SAMPLE_TID)
evsel.open(cpus = cpus, threads = threads);
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 011ed267660..e5a462f1d07 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -315,7 +315,7 @@ fallback:
"Please use:\n\n"
" perf buildid-cache -av vmlinux\n\n"
"or:\n\n"
- " --vmlinux vmlinux",
+ " --vmlinux vmlinux\n",
sym->name, build_id_msg ?: "");
goto out_free_filename;
}
diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c
index 5e230acae1e..0a1adc1111f 100644
--- a/tools/perf/util/bitmap.c
+++ b/tools/perf/util/bitmap.c
@@ -19,3 +19,13 @@ int __bitmap_weight(const unsigned long *bitmap, int bits)
return w;
}
+
+void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
+ const unsigned long *bitmap2, int bits)
+{
+ int k;
+ int nr = BITS_TO_LONGS(bits);
+
+ for (k = 0; k < nr; k++)
+ dst[k] = bitmap1[k] | bitmap2[k];
+}
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 6893eec693a..adc72f09914 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -166,6 +166,17 @@ out:
return cpus;
}
+size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp)
+{
+ int i;
+ size_t printed = fprintf(fp, "%d cpu%s: ",
+ map->nr, map->nr > 1 ? "s" : "");
+ for (i = 0; i < map->nr; ++i)
+ printed += fprintf(fp, "%s%d", i ? ", " : "", map->map[i]);
+
+ return printed + fprintf(fp, "\n");
+}
+
struct cpu_map *cpu_map__dummy_new(void)
{
struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int));
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 072c0a37479..c41518573c6 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -1,6 +1,8 @@
#ifndef __PERF_CPUMAP_H
#define __PERF_CPUMAP_H
+#include <stdio.h>
+
struct cpu_map {
int nr;
int map[];
@@ -10,4 +12,6 @@ struct cpu_map *cpu_map__new(const char *cpu_list);
struct cpu_map *cpu_map__dummy_new(void);
void cpu_map__delete(struct cpu_map *map);
+size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
+
#endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c
index 35073621e5d..aada3ac5e89 100644
--- a/tools/perf/util/ctype.c
+++ b/tools/perf/util/ctype.c
@@ -3,7 +3,7 @@
*
* No surprises, and works with signed and unsigned chars.
*/
-#include "cache.h"
+#include "util.h"
enum {
S = GIT_SPACE,
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c
index ffc35e748e8..dd8b19319c0 100644
--- a/tools/perf/util/debugfs.c
+++ b/tools/perf/util/debugfs.c
@@ -15,32 +15,6 @@ static const char *debugfs_known_mountpoints[] = {
0,
};
-/* use this to force a umount */
-void debugfs_force_cleanup(void)
-{
- debugfs_find_mountpoint();
- debugfs_premounted = 0;
- debugfs_umount();
-}
-
-/* construct a full path to a debugfs element */
-int debugfs_make_path(const char *element, char *buffer, int size)
-{
- int len;
-
- if (strlen(debugfs_mountpoint) == 0) {
- buffer[0] = '\0';
- return -1;
- }
-
- len = strlen(debugfs_mountpoint) + strlen(element) + 1;
- if (len >= size)
- return len+1;
-
- snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element);
- return 0;
-}
-
static int debugfs_found;
/* find the path to the mounted debugfs */
@@ -97,17 +71,6 @@ int debugfs_valid_mountpoint(const char *debugfs)
return 0;
}
-
-int debugfs_valid_entry(const char *path)
-{
- struct stat st;
-
- if (stat(path, &st))
- return -errno;
-
- return 0;
-}
-
static void debugfs_set_tracing_events_path(const char *mountpoint)
{
snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s",
@@ -149,107 +112,3 @@ void debugfs_set_path(const char *mountpoint)
snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint);
debugfs_set_tracing_events_path(mountpoint);
}
-
-/* umount the debugfs */
-
-int debugfs_umount(void)
-{
- char umountcmd[128];
- int ret;
-
- /* if it was already mounted, leave it */
- if (debugfs_premounted)
- return 0;
-
- /* make sure it's a valid mount point */
- ret = debugfs_valid_mountpoint(debugfs_mountpoint);
- if (ret)
- return ret;
-
- snprintf(umountcmd, sizeof(umountcmd),
- "/bin/umount %s", debugfs_mountpoint);
- return system(umountcmd);
-}
-
-int debugfs_write(const char *entry, const char *value)
-{
- char path[PATH_MAX + 1];
- int ret, count;
- int fd;
-
- /* construct the path */
- snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
-
- /* verify that it exists */
- ret = debugfs_valid_entry(path);
- if (ret)
- return ret;
-
- /* get how many chars we're going to write */
- count = strlen(value);
-
- /* open the debugfs entry */
- fd = open(path, O_RDWR);
- if (fd < 0)
- return -errno;
-
- while (count > 0) {
- /* write it */
- ret = write(fd, value, count);
- if (ret <= 0) {
- if (ret == EAGAIN)
- continue;
- close(fd);
- return -errno;
- }
- count -= ret;
- }
-
- /* close it */
- close(fd);
-
- /* return success */
- return 0;
-}
-
-/*
- * read a debugfs entry
- * returns the number of chars read or a negative errno
- */
-int debugfs_read(const char *entry, char *buffer, size_t size)
-{
- char path[PATH_MAX + 1];
- int ret;
- int fd;
-
- /* construct the path */
- snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry);
-
- /* verify that it exists */
- ret = debugfs_valid_entry(path);
- if (ret)
- return ret;
-
- /* open the debugfs entry */
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return -errno;
-
- do {
- /* read it */
- ret = read(fd, buffer, size);
- if (ret == 0) {
- close(fd);
- return EOF;
- }
- } while (ret < 0 && errno == EAGAIN);
-
- /* close it */
- close(fd);
-
- /* make *sure* there's a null character at the end */
- buffer[ret] = '\0';
-
- /* return the number of chars read */
- return ret;
-}
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h
index 4a878f735eb..68f3e87ec57 100644
--- a/tools/perf/util/debugfs.h
+++ b/tools/perf/util/debugfs.h
@@ -3,14 +3,8 @@
const char *debugfs_find_mountpoint(void);
int debugfs_valid_mountpoint(const char *debugfs);
-int debugfs_valid_entry(const char *path);
char *debugfs_mount(const char *mountpoint);
-int debugfs_umount(void);
void debugfs_set_path(const char *mountpoint);
-int debugfs_write(const char *entry, const char *value);
-int debugfs_read(const char *entry, char *buffer, size_t size);
-void debugfs_force_cleanup(void);
-int debugfs_make_path(const char *element, char *buffer, int size);
extern char debugfs_mountpoint[];
extern char tracing_events_path[];
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index cbdeaad9c5e..1b197280c62 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -81,6 +81,7 @@ struct perf_sample {
u32 raw_size;
void *raw_data;
struct ip_callchain *callchain;
+ struct branch_stack *branch_stack;
};
#define BUILD_ID_SIZE 20
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index ea32a061f1c..159263d17c2 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -97,9 +97,9 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
++evlist->nr_entries;
}
-static void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
- struct list_head *list,
- int nr_entries)
+void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
+ struct list_head *list,
+ int nr_entries)
{
list_splice_tail(list, &evlist->entries);
evlist->nr_entries += nr_entries;
@@ -597,15 +597,15 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
return perf_evlist__mmap_per_cpu(evlist, prot, mask);
}
-int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid,
- pid_t target_tid, const char *cpu_list)
+int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid,
+ const char *target_tid, uid_t uid, const char *cpu_list)
{
- evlist->threads = thread_map__new(target_pid, target_tid);
+ evlist->threads = thread_map__new_str(target_pid, target_tid, uid);
if (evlist->threads == NULL)
return -1;
- if (cpu_list == NULL && target_tid != -1)
+ if (uid != UINT_MAX || (cpu_list == NULL && target_tid))
evlist->cpus = cpu_map__dummy_new();
else
evlist->cpus = cpu_map__new(cpu_list);
@@ -765,6 +765,7 @@ out_err:
list_for_each_entry_reverse(evsel, &evlist->entries, node)
perf_evsel__close(evsel, ncpus, nthreads);
+ errno = -err;
return err;
}
@@ -824,7 +825,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist,
exit(-1);
}
- if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1)
+ if (!opts->system_wide && !opts->target_tid && !opts->target_pid)
evlist->threads->map[0] = evlist->workload.pid;
close(child_ready_pipe[1]);
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 8922aeed046..21f1c9e57f1 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -106,8 +106,8 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
evlist->threads = threads;
}
-int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid,
- pid_t target_tid, const char *cpu_list);
+int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid,
+ const char *tid, uid_t uid, const char *cpu_list);
void perf_evlist__delete_maps(struct perf_evlist *evlist);
int perf_evlist__set_filters(struct perf_evlist *evlist);
@@ -117,4 +117,9 @@ u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist);
bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist);
bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist);
+
+void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
+ struct list_head *list,
+ int nr_entries);
+
#endif /* __PERF_EVLIST_H */
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 7132ee834e0..f421f7cbc0d 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -68,7 +68,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
struct perf_event_attr *attr = &evsel->attr;
int track = !evsel->idx; /* only the first counter needs these */
- attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0;
+ attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1;
attr->inherit = !opts->no_inherit;
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING |
@@ -111,7 +111,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
if (opts->period)
attr->sample_type |= PERF_SAMPLE_PERIOD;
- if (opts->sample_id_all_avail &&
+ if (!opts->sample_id_all_missing &&
(opts->sample_time || opts->system_wide ||
!opts->no_inherit || opts->cpu_list))
attr->sample_type |= PERF_SAMPLE_TIME;
@@ -126,11 +126,15 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
attr->watermark = 0;
attr->wakeup_events = 1;
}
+ if (opts->branch_stack) {
+ attr->sample_type |= PERF_SAMPLE_BRANCH_STACK;
+ attr->branch_sample_type = opts->branch_stack;
+ }
attr->mmap = track;
attr->comm = track;
- if (opts->target_pid == -1 && opts->target_tid == -1 && !opts->system_wide) {
+ if (!opts->target_pid && !opts->target_tid && !opts->system_wide) {
attr->disabled = 1;
attr->enable_on_exec = 1;
}
@@ -536,7 +540,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
}
if (type & PERF_SAMPLE_READ) {
- fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n");
+ fprintf(stderr, "PERF_SAMPLE_READ is unsupported for now\n");
return -1;
}
@@ -576,6 +580,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
data->raw_data = (void *) pdata;
}
+ if (type & PERF_SAMPLE_BRANCH_STACK) {
+ u64 sz;
+
+ data->branch_stack = (struct branch_stack *)array;
+ array++; /* nr */
+
+ sz = data->branch_stack->nr * sizeof(struct branch_entry);
+ sz /= sizeof(u64);
+ array += sz;
+ }
return 0;
}
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 14bb035c5fd..fcd9cf3ea63 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -63,9 +63,20 @@ char *perf_header__find_event(u64 id)
return NULL;
}
-static const char *__perf_magic = "PERFFILE";
+/*
+ * magic2 = "PERFILE2"
+ * must be a numerical value to let the endianness
+ * determine the memory layout. That way we are able
+ * to detect endianness when reading the perf.data file
+ * back.
+ *
+ * we check for legacy (PERFFILE) format.
+ */
+static const char *__perf_magic1 = "PERFFILE";
+static const u64 __perf_magic2 = 0x32454c4946524550ULL;
+static const u64 __perf_magic2_sw = 0x50455246494c4532ULL;
-#define PERF_MAGIC (*(u64 *)__perf_magic)
+#define PERF_MAGIC __perf_magic2
struct perf_file_attr {
struct perf_event_attr attr;
@@ -1012,6 +1023,12 @@ write_it:
return do_write_string(fd, buffer);
}
+static int write_branch_stack(int fd __used, struct perf_header *h __used,
+ struct perf_evlist *evlist __used)
+{
+ return 0;
+}
+
static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
{
char *str = do_read_string(fd, ph);
@@ -1133,8 +1150,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
uint64_t id;
void *buf = NULL;
char *str;
- u32 nre, sz, nr, i, j, msz;
- int ret;
+ u32 nre, sz, nr, i, j;
+ ssize_t ret;
+ size_t msz;
/* number of events */
ret = read(fd, &nre, sizeof(nre));
@@ -1151,25 +1169,23 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
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 */
+ /* buffer to hold on file attr struct */
buf = malloc(sz);
if (!buf)
goto error;
msz = sizeof(attr);
- if (sz < msz)
+ if (sz < (ssize_t)msz)
msz = sz;
for (i = 0 ; i < nre; i++) {
+ /*
+ * must read entire on-file attr struct to
+ * sync up with layout.
+ */
ret = read(fd, buf, sz);
if (ret != (ssize_t)sz)
goto error;
@@ -1305,25 +1321,204 @@ static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
free(str);
}
+static void print_branch_stack(struct perf_header *ph __used, int fd __used,
+ FILE *fp)
+{
+ fprintf(fp, "# contains samples with branch stack\n");
+}
+
+static int __event_process_build_id(struct build_id_event *bev,
+ char *filename,
+ struct perf_session *session)
+{
+ int err = -1;
+ struct list_head *head;
+ struct machine *machine;
+ u16 misc;
+ struct dso *dso;
+ enum dso_kernel_type dso_type;
+
+ machine = perf_session__findnew_machine(session, bev->pid);
+ if (!machine)
+ goto out;
+
+ misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+
+ switch (misc) {
+ case PERF_RECORD_MISC_KERNEL:
+ dso_type = DSO_TYPE_KERNEL;
+ head = &machine->kernel_dsos;
+ break;
+ case PERF_RECORD_MISC_GUEST_KERNEL:
+ dso_type = DSO_TYPE_GUEST_KERNEL;
+ head = &machine->kernel_dsos;
+ break;
+ case PERF_RECORD_MISC_USER:
+ case PERF_RECORD_MISC_GUEST_USER:
+ dso_type = DSO_TYPE_USER;
+ head = &machine->user_dsos;
+ break;
+ default:
+ goto out;
+ }
+
+ dso = __dsos__findnew(head, filename);
+ if (dso != NULL) {
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ dso__set_build_id(dso, &bev->build_id);
+
+ if (filename[0] == '[')
+ dso->kernel = dso_type;
+
+ build_id__sprintf(dso->build_id, sizeof(dso->build_id),
+ sbuild_id);
+ pr_debug("build id event received for %s: %s\n",
+ dso->long_name, sbuild_id);
+ }
+
+ err = 0;
+out:
+ return err;
+}
+
+static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
+ int input, u64 offset, u64 size)
+{
+ struct perf_session *session = container_of(header, struct perf_session, header);
+ struct {
+ struct perf_event_header header;
+ u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+ char filename[0];
+ } old_bev;
+ struct build_id_event bev;
+ char filename[PATH_MAX];
+ u64 limit = offset + size;
+
+ while (offset < limit) {
+ ssize_t len;
+
+ if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
+ return -1;
+
+ if (header->needs_swap)
+ perf_event_header__bswap(&old_bev.header);
+
+ len = old_bev.header.size - sizeof(old_bev);
+ if (read(input, filename, len) != len)
+ return -1;
+
+ bev.header = old_bev.header;
+
+ /*
+ * As the pid is the missing value, we need to fill
+ * it properly. The header.misc value give us nice hint.
+ */
+ bev.pid = HOST_KERNEL_ID;
+ if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER ||
+ bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL)
+ bev.pid = DEFAULT_GUEST_KERNEL_ID;
+
+ memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id));
+ __event_process_build_id(&bev, filename, session);
+
+ offset += bev.header.size;
+ }
+
+ return 0;
+}
+
+static int perf_header__read_build_ids(struct perf_header *header,
+ int input, u64 offset, u64 size)
+{
+ struct perf_session *session = container_of(header, struct perf_session, header);
+ struct build_id_event bev;
+ char filename[PATH_MAX];
+ u64 limit = offset + size, orig_offset = offset;
+ int err = -1;
+
+ while (offset < limit) {
+ ssize_t len;
+
+ if (read(input, &bev, sizeof(bev)) != sizeof(bev))
+ goto out;
+
+ if (header->needs_swap)
+ perf_event_header__bswap(&bev.header);
+
+ len = bev.header.size - sizeof(bev);
+ if (read(input, filename, len) != len)
+ goto out;
+ /*
+ * The a1645ce1 changeset:
+ *
+ * "perf: 'perf kvm' tool for monitoring guest performance from host"
+ *
+ * Added a field to struct build_id_event that broke the file
+ * format.
+ *
+ * Since the kernel build-id is the first entry, process the
+ * table using the old format if the well known
+ * '[kernel.kallsyms]' string for the kernel build-id has the
+ * first 4 characters chopped off (where the pid_t sits).
+ */
+ if (memcmp(filename, "nel.kallsyms]", 13) == 0) {
+ if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1)
+ return -1;
+ return perf_header__read_build_ids_abi_quirk(header, input, offset, size);
+ }
+
+ __event_process_build_id(&bev, filename, session);
+
+ offset += bev.header.size;
+ }
+ err = 0;
+out:
+ return err;
+}
+
+static int process_trace_info(struct perf_file_section *section __unused,
+ struct perf_header *ph __unused,
+ int feat __unused, int fd)
+{
+ trace_report(fd, false);
+ return 0;
+}
+
+static int process_build_id(struct perf_file_section *section,
+ struct perf_header *ph,
+ int feat __unused, int fd)
+{
+ if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
+ pr_debug("Failed to read buildids, continuing...\n");
+ return 0;
+}
+
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);
+ int (*process)(struct perf_file_section *section,
+ struct perf_header *h, int feat, int fd);
const char *name;
bool full_only;
};
#define FEAT_OPA(n, func) \
[n] = { .name = #n, .write = write_##func, .print = print_##func }
+#define FEAT_OPP(n, func) \
+ [n] = { .name = #n, .write = write_##func, .print = print_##func, \
+ .process = process_##func }
#define FEAT_OPF(n, func) \
- [n] = { .name = #n, .write = write_##func, .print = print_##func, .full_only = true }
+ [n] = { .name = #n, .write = write_##func, .print = print_##func, \
+ .full_only = true }
/* feature_ops not implemented: */
#define print_trace_info NULL
#define print_build_id NULL
static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
- FEAT_OPA(HEADER_TRACE_INFO, trace_info),
- FEAT_OPA(HEADER_BUILD_ID, build_id),
+ FEAT_OPP(HEADER_TRACE_INFO, trace_info),
+ FEAT_OPP(HEADER_BUILD_ID, build_id),
FEAT_OPA(HEADER_HOSTNAME, hostname),
FEAT_OPA(HEADER_OSRELEASE, osrelease),
FEAT_OPA(HEADER_VERSION, version),
@@ -1336,6 +1531,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPA(HEADER_CMDLINE, cmdline),
FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology),
FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology),
+ FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
};
struct header_print_data {
@@ -1620,24 +1816,128 @@ out_free:
return err;
}
+static const int attr_file_abi_sizes[] = {
+ [0] = PERF_ATTR_SIZE_VER0,
+ [1] = PERF_ATTR_SIZE_VER1,
+ 0,
+};
+
+/*
+ * In the legacy file format, the magic number is not used to encode endianness.
+ * hdr_sz was used to encode endianness. But given that hdr_sz can vary based
+ * on ABI revisions, we need to try all combinations for all endianness to
+ * detect the endianness.
+ */
+static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph)
+{
+ uint64_t ref_size, attr_size;
+ int i;
+
+ for (i = 0 ; attr_file_abi_sizes[i]; i++) {
+ ref_size = attr_file_abi_sizes[i]
+ + sizeof(struct perf_file_section);
+ if (hdr_sz != ref_size) {
+ attr_size = bswap_64(hdr_sz);
+ if (attr_size != ref_size)
+ continue;
+
+ ph->needs_swap = true;
+ }
+ pr_debug("ABI%d perf.data file detected, need_swap=%d\n",
+ i,
+ ph->needs_swap);
+ return 0;
+ }
+ /* could not determine endianness */
+ return -1;
+}
+
+#define PERF_PIPE_HDR_VER0 16
+
+static const size_t attr_pipe_abi_sizes[] = {
+ [0] = PERF_PIPE_HDR_VER0,
+ 0,
+};
+
+/*
+ * In the legacy pipe format, there is an implicit assumption that endiannesss
+ * between host recording the samples, and host parsing the samples is the
+ * same. This is not always the case given that the pipe output may always be
+ * redirected into a file and analyzed on a different machine with possibly a
+ * different endianness and perf_event ABI revsions in the perf tool itself.
+ */
+static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph)
+{
+ u64 attr_size;
+ int i;
+
+ for (i = 0 ; attr_pipe_abi_sizes[i]; i++) {
+ if (hdr_sz != attr_pipe_abi_sizes[i]) {
+ attr_size = bswap_64(hdr_sz);
+ if (attr_size != hdr_sz)
+ continue;
+
+ ph->needs_swap = true;
+ }
+ pr_debug("Pipe ABI%d perf.data file detected\n", i);
+ return 0;
+ }
+ return -1;
+}
+
+static int check_magic_endian(u64 magic, uint64_t hdr_sz,
+ bool is_pipe, struct perf_header *ph)
+{
+ int ret;
+
+ /* check for legacy format */
+ ret = memcmp(&magic, __perf_magic1, sizeof(magic));
+ if (ret == 0) {
+ pr_debug("legacy perf.data format\n");
+ if (is_pipe)
+ return try_all_pipe_abis(hdr_sz, ph);
+
+ return try_all_file_abis(hdr_sz, ph);
+ }
+ /*
+ * the new magic number serves two purposes:
+ * - unique number to identify actual perf.data files
+ * - encode endianness of file
+ */
+
+ /* check magic number with one endianness */
+ if (magic == __perf_magic2)
+ return 0;
+
+ /* check magic number with opposite endianness */
+ if (magic != __perf_magic2_sw)
+ return -1;
+
+ ph->needs_swap = true;
+
+ return 0;
+}
+
int perf_file_header__read(struct perf_file_header *header,
struct perf_header *ph, int fd)
{
+ int ret;
+
lseek(fd, 0, SEEK_SET);
- if (readn(fd, header, sizeof(*header)) <= 0 ||
- memcmp(&header->magic, __perf_magic, sizeof(header->magic)))
+ ret = readn(fd, header, sizeof(*header));
+ if (ret <= 0)
return -1;
- if (header->attr_size != sizeof(struct perf_file_attr)) {
- u64 attr_size = bswap_64(header->attr_size);
-
- if (attr_size != sizeof(struct perf_file_attr))
- return -1;
+ if (check_magic_endian(header->magic,
+ header->attr_size, false, ph) < 0) {
+ pr_debug("magic/endian check failed\n");
+ return -1;
+ }
+ if (ph->needs_swap) {
mem_bswap_64(header, offsetof(struct perf_file_header,
- adds_features));
- ph->needs_swap = true;
+ adds_features));
}
if (header->size != sizeof(*header)) {
@@ -1689,156 +1989,6 @@ int perf_file_header__read(struct perf_file_header *header,
return 0;
}
-static int __event_process_build_id(struct build_id_event *bev,
- char *filename,
- struct perf_session *session)
-{
- int err = -1;
- struct list_head *head;
- struct machine *machine;
- u16 misc;
- struct dso *dso;
- enum dso_kernel_type dso_type;
-
- machine = perf_session__findnew_machine(session, bev->pid);
- if (!machine)
- goto out;
-
- misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
-
- switch (misc) {
- case PERF_RECORD_MISC_KERNEL:
- dso_type = DSO_TYPE_KERNEL;
- head = &machine->kernel_dsos;
- break;
- case PERF_RECORD_MISC_GUEST_KERNEL:
- dso_type = DSO_TYPE_GUEST_KERNEL;
- head = &machine->kernel_dsos;
- break;
- case PERF_RECORD_MISC_USER:
- case PERF_RECORD_MISC_GUEST_USER:
- dso_type = DSO_TYPE_USER;
- head = &machine->user_dsos;
- break;
- default:
- goto out;
- }
-
- dso = __dsos__findnew(head, filename);
- if (dso != NULL) {
- char sbuild_id[BUILD_ID_SIZE * 2 + 1];
-
- dso__set_build_id(dso, &bev->build_id);
-
- if (filename[0] == '[')
- dso->kernel = dso_type;
-
- build_id__sprintf(dso->build_id, sizeof(dso->build_id),
- sbuild_id);
- pr_debug("build id event received for %s: %s\n",
- dso->long_name, sbuild_id);
- }
-
- err = 0;
-out:
- return err;
-}
-
-static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
- int input, u64 offset, u64 size)
-{
- struct perf_session *session = container_of(header, struct perf_session, header);
- struct {
- struct perf_event_header header;
- u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
- char filename[0];
- } old_bev;
- struct build_id_event bev;
- char filename[PATH_MAX];
- u64 limit = offset + size;
-
- while (offset < limit) {
- ssize_t len;
-
- if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
- return -1;
-
- if (header->needs_swap)
- perf_event_header__bswap(&old_bev.header);
-
- len = old_bev.header.size - sizeof(old_bev);
- if (read(input, filename, len) != len)
- return -1;
-
- bev.header = old_bev.header;
-
- /*
- * As the pid is the missing value, we need to fill
- * it properly. The header.misc value give us nice hint.
- */
- bev.pid = HOST_KERNEL_ID;
- if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER ||
- bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL)
- bev.pid = DEFAULT_GUEST_KERNEL_ID;
-
- memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id));
- __event_process_build_id(&bev, filename, session);
-
- offset += bev.header.size;
- }
-
- return 0;
-}
-
-static int perf_header__read_build_ids(struct perf_header *header,
- int input, u64 offset, u64 size)
-{
- struct perf_session *session = container_of(header, struct perf_session, header);
- struct build_id_event bev;
- char filename[PATH_MAX];
- u64 limit = offset + size, orig_offset = offset;
- int err = -1;
-
- while (offset < limit) {
- ssize_t len;
-
- if (read(input, &bev, sizeof(bev)) != sizeof(bev))
- goto out;
-
- if (header->needs_swap)
- perf_event_header__bswap(&bev.header);
-
- len = bev.header.size - sizeof(bev);
- if (read(input, filename, len) != len)
- goto out;
- /*
- * The a1645ce1 changeset:
- *
- * "perf: 'perf kvm' tool for monitoring guest performance from host"
- *
- * Added a field to struct build_id_event that broke the file
- * format.
- *
- * Since the kernel build-id is the first entry, process the
- * table using the old format if the well known
- * '[kernel.kallsyms]' string for the kernel build-id has the
- * first 4 characters chopped off (where the pid_t sits).
- */
- if (memcmp(filename, "nel.kallsyms]", 13) == 0) {
- if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1)
- return -1;
- return perf_header__read_build_ids_abi_quirk(header, input, offset, size);
- }
-
- __event_process_build_id(&bev, filename, session);
-
- offset += bev.header.size;
- }
- err = 0;
-out:
- return err;
-}
-
static int perf_file_section__process(struct perf_file_section *section,
struct perf_header *ph,
int feat, int fd, void *data __used)
@@ -1854,40 +2004,32 @@ static int perf_file_section__process(struct perf_file_section *section,
return 0;
}
- switch (feat) {
- case HEADER_TRACE_INFO:
- trace_report(fd, false);
- break;
- case HEADER_BUILD_ID:
- if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
- pr_debug("Failed to read buildids, continuing...\n");
- break;
- default:
- break;
- }
+ if (!feat_ops[feat].process)
+ return 0;
- return 0;
+ return feat_ops[feat].process(section, ph, feat, fd);
}
static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
struct perf_header *ph, int fd,
bool repipe)
{
- if (readn(fd, header, sizeof(*header)) <= 0 ||
- memcmp(&header->magic, __perf_magic, sizeof(header->magic)))
- return -1;
+ int ret;
- if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
+ ret = readn(fd, header, sizeof(*header));
+ if (ret <= 0)
return -1;
- if (header->size != sizeof(*header)) {
- u64 size = bswap_64(header->size);
+ if (check_magic_endian(header->magic, header->size, true, ph) < 0) {
+ pr_debug("endian/magic failed\n");
+ return -1;
+ }
- if (size != sizeof(*header))
- return -1;
+ if (ph->needs_swap)
+ header->size = bswap_64(header->size);
- ph->needs_swap = true;
- }
+ if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
+ return -1;
return 0;
}
@@ -1908,6 +2050,52 @@ static int perf_header__read_pipe(struct perf_session *session, int fd)
return 0;
}
+static int read_attr(int fd, struct perf_header *ph,
+ struct perf_file_attr *f_attr)
+{
+ struct perf_event_attr *attr = &f_attr->attr;
+ size_t sz, left;
+ size_t our_sz = sizeof(f_attr->attr);
+ int ret;
+
+ memset(f_attr, 0, sizeof(*f_attr));
+
+ /* read minimal guaranteed structure */
+ ret = readn(fd, attr, PERF_ATTR_SIZE_VER0);
+ if (ret <= 0) {
+ pr_debug("cannot read %d bytes of header attr\n",
+ PERF_ATTR_SIZE_VER0);
+ return -1;
+ }
+
+ /* on file perf_event_attr size */
+ sz = attr->size;
+
+ if (ph->needs_swap)
+ sz = bswap_32(sz);
+
+ if (sz == 0) {
+ /* assume ABI0 */
+ sz = PERF_ATTR_SIZE_VER0;
+ } else if (sz > our_sz) {
+ pr_debug("file uses a more recent and unsupported ABI"
+ " (%zu bytes extra)\n", sz - our_sz);
+ return -1;
+ }
+ /* what we have not yet read and that we know about */
+ left = sz - PERF_ATTR_SIZE_VER0;
+ if (left) {
+ void *ptr = attr;
+ ptr += PERF_ATTR_SIZE_VER0;
+
+ ret = readn(fd, ptr, left);
+ }
+ /* read perf_file_section, ids are read in caller */
+ ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids));
+
+ return ret <= 0 ? -1 : 0;
+}
+
int perf_session__read_header(struct perf_session *session, int fd)
{
struct perf_header *header = &session->header;
@@ -1923,19 +2111,17 @@ int perf_session__read_header(struct perf_session *session, int fd)
if (session->fd_pipe)
return perf_header__read_pipe(session, fd);
- if (perf_file_header__read(&f_header, header, fd) < 0) {
- pr_debug("incompatible file format\n");
+ if (perf_file_header__read(&f_header, header, fd) < 0)
return -EINVAL;
- }
- nr_attrs = f_header.attrs.size / sizeof(f_attr);
+ nr_attrs = f_header.attrs.size / f_header.attr_size;
lseek(fd, f_header.attrs.offset, SEEK_SET);
for (i = 0; i < nr_attrs; i++) {
struct perf_evsel *evsel;
off_t tmp;
- if (readn(fd, &f_attr, sizeof(f_attr)) <= 0)
+ if (read_attr(fd, header, &f_attr) < 0)
goto out_errno;
if (header->needs_swap)
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index ac4ec956024..21a6be09c12 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -11,6 +11,7 @@
enum {
HEADER_RESERVED = 0, /* always cleared */
+ HEADER_FIRST_FEATURE = 1,
HEADER_TRACE_INFO = 1,
HEADER_BUILD_ID,
@@ -26,7 +27,7 @@ enum {
HEADER_EVENT_DESC,
HEADER_CPU_TOPOLOGY,
HEADER_NUMA_TOPOLOGY,
-
+ HEADER_BRANCH_STACK,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index e11e482bd18..3dc99a9b71f 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -50,21 +50,25 @@ static void hists__reset_col_len(struct hists *hists)
hists__set_col_len(hists, col, 0);
}
+static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
+{
+ const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
+
+ if (hists__col_len(hists, dso) < unresolved_col_width &&
+ !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ !symbol_conf.dso_list)
+ hists__set_col_len(hists, dso, unresolved_col_width);
+}
+
static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
{
+ const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
u16 len;
if (h->ms.sym)
- hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen);
- else {
- const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
-
- if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
- !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
- !symbol_conf.dso_list)
- hists__set_col_len(hists, HISTC_DSO,
- unresolved_col_width);
- }
+ hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4);
+ else
+ hists__set_unres_dso_col_len(hists, HISTC_DSO);
len = thread__comm_len(h->thread);
if (hists__new_col_len(hists, HISTC_COMM, len))
@@ -74,6 +78,37 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
len = dso__name_len(h->ms.map->dso);
hists__new_col_len(hists, HISTC_DSO, len);
}
+
+ if (h->branch_info) {
+ int symlen;
+ /*
+ * +4 accounts for '[x] ' priv level info
+ * +2 account of 0x prefix on raw addresses
+ */
+ if (h->branch_info->from.sym) {
+ symlen = (int)h->branch_info->from.sym->namelen + 4;
+ hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
+
+ symlen = dso__name_len(h->branch_info->from.map->dso);
+ hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
+ } else {
+ symlen = unresolved_col_width + 4 + 2;
+ hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
+ hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
+ }
+
+ if (h->branch_info->to.sym) {
+ symlen = (int)h->branch_info->to.sym->namelen + 4;
+ hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
+
+ symlen = dso__name_len(h->branch_info->to.map->dso);
+ hists__new_col_len(hists, HISTC_DSO_TO, symlen);
+ } else {
+ symlen = unresolved_col_width + 4 + 2;
+ hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
+ hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
+ }
+ }
}
static void hist_entry__add_cpumode_period(struct hist_entry *he,
@@ -195,26 +230,14 @@ static u8 symbol__parent_filter(const struct symbol *parent)
return 0;
}
-struct hist_entry *__hists__add_entry(struct hists *hists,
+static struct hist_entry *add_hist_entry(struct hists *hists,
+ struct hist_entry *entry,
struct addr_location *al,
- struct symbol *sym_parent, u64 period)
+ u64 period)
{
struct rb_node **p;
struct rb_node *parent = NULL;
struct hist_entry *he;
- struct hist_entry entry = {
- .thread = al->thread,
- .ms = {
- .map = al->map,
- .sym = al->sym,
- },
- .cpu = al->cpu,
- .ip = al->addr,
- .level = al->level,
- .period = period,
- .parent = sym_parent,
- .filtered = symbol__parent_filter(sym_parent),
- };
int cmp;
pthread_mutex_lock(&hists->lock);
@@ -225,7 +248,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
parent = *p;
he = rb_entry(parent, struct hist_entry, rb_node_in);
- cmp = hist_entry__cmp(&entry, he);
+ cmp = hist_entry__cmp(entry, he);
if (!cmp) {
he->period += period;
@@ -239,7 +262,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
p = &(*p)->rb_right;
}
- he = hist_entry__new(&entry);
+ he = hist_entry__new(entry);
if (!he)
goto out_unlock;
@@ -252,6 +275,51 @@ out_unlock:
return he;
}
+struct hist_entry *__hists__add_branch_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *sym_parent,
+ struct branch_info *bi,
+ u64 period)
+{
+ struct hist_entry entry = {
+ .thread = al->thread,
+ .ms = {
+ .map = bi->to.map,
+ .sym = bi->to.sym,
+ },
+ .cpu = al->cpu,
+ .ip = bi->to.addr,
+ .level = al->level,
+ .period = period,
+ .parent = sym_parent,
+ .filtered = symbol__parent_filter(sym_parent),
+ .branch_info = bi,
+ };
+
+ return add_hist_entry(self, &entry, al, period);
+}
+
+struct hist_entry *__hists__add_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *sym_parent, u64 period)
+{
+ struct hist_entry entry = {
+ .thread = al->thread,
+ .ms = {
+ .map = al->map,
+ .sym = al->sym,
+ },
+ .cpu = al->cpu,
+ .ip = al->addr,
+ .level = al->level,
+ .period = period,
+ .parent = sym_parent,
+ .filtered = symbol__parent_filter(sym_parent),
+ };
+
+ return add_hist_entry(self, &entry, al, period);
+}
+
int64_t
hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
{
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index f55f0a8d1f8..9413f3e31fe 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -32,6 +32,7 @@ struct events_stats {
u32 nr_unknown_events;
u32 nr_invalid_chains;
u32 nr_unknown_id;
+ u32 nr_unprocessable_samples;
};
enum hist_column {
@@ -41,6 +42,11 @@ enum hist_column {
HISTC_COMM,
HISTC_PARENT,
HISTC_CPU,
+ HISTC_MISPREDICT,
+ HISTC_SYMBOL_FROM,
+ HISTC_SYMBOL_TO,
+ HISTC_DSO_FROM,
+ HISTC_DSO_TO,
HISTC_NR_COLS, /* Last entry */
};
@@ -55,6 +61,7 @@ struct hists {
u64 nr_entries;
const struct thread *thread_filter;
const struct dso *dso_filter;
+ const char *uid_filter_str;
pthread_mutex_t lock;
struct events_stats stats;
u64 event_stream;
@@ -72,6 +79,12 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
struct hists *hists);
void hist_entry__free(struct hist_entry *);
+struct hist_entry *__hists__add_branch_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *sym_parent,
+ struct branch_info *bi,
+ u64 period);
+
void hists__output_resort(struct hists *self);
void hists__output_resort_threaded(struct hists *hists);
void hists__collapse_resort(struct hists *self);
diff --git a/tools/perf/util/include/asm/dwarf2.h b/tools/perf/util/include/asm/dwarf2.h
index bb4198e7837..afe38199e92 100644
--- a/tools/perf/util/include/asm/dwarf2.h
+++ b/tools/perf/util/include/asm/dwarf2.h
@@ -2,10 +2,12 @@
#ifndef PERF_DWARF2_H
#define PERF_DWARF2_H
-/* dwarf2.h ... dummy header file for including arch/x86/lib/memcpy_64.S */
+/* dwarf2.h ... dummy header file for including arch/x86/lib/mem{cpy,set}_64.S */
#define CFI_STARTPROC
#define CFI_ENDPROC
+#define CFI_REMEMBER_STATE
+#define CFI_RESTORE_STATE
#endif /* PERF_DWARF2_H */
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h
index eda4416efa0..bb162e40c76 100644
--- a/tools/perf/util/include/linux/bitmap.h
+++ b/tools/perf/util/include/linux/bitmap.h
@@ -5,6 +5,8 @@
#include <linux/bitops.h>
int __bitmap_weight(const unsigned long *bitmap, int bits);
+void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
+ const unsigned long *bitmap2, int bits);
#define BITMAP_LAST_WORD_MASK(nbits) \
( \
@@ -32,4 +34,13 @@ static inline int bitmap_weight(const unsigned long *src, int nbits)
return __bitmap_weight(src, nbits);
}
+static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
+ const unsigned long *src2, int nbits)
+{
+ if (small_const_nbits(nbits))
+ *dst = *src1 | *src2;
+ else
+ __bitmap_or(dst, src1, src2, nbits);
+}
+
#endif /* _PERF_BITOPS_H */
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h
index 62cdee78db7..f1584833bd2 100644
--- a/tools/perf/util/include/linux/bitops.h
+++ b/tools/perf/util/include/linux/bitops.h
@@ -15,7 +15,7 @@
(bit) = find_next_bit((addr), (size), (bit) + 1))
/* same as for_each_set_bit() but use bit as value to start with */
-#define for_each_set_bit_cont(bit, addr, size) \
+#define for_each_set_bit_from(bit, addr, size) \
for ((bit) = find_next_bit((addr), (size), (bit)); \
(bit) < (size); \
(bit) = find_next_bit((addr), (size), (bit) + 1))
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 316aa0ab712..dea6d1c1a95 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -212,6 +212,21 @@ size_t map__fprintf(struct map *self, FILE *fp)
self->start, self->end, self->pgoff, self->dso->name);
}
+size_t map__fprintf_dsoname(struct map *map, FILE *fp)
+{
+ const char *dsoname;
+
+ if (map && map->dso && (map->dso->name || map->dso->long_name)) {
+ if (symbol_conf.show_kernel_path && map->dso->long_name)
+ dsoname = map->dso->long_name;
+ else if (map->dso->name)
+ dsoname = map->dso->name;
+ } else
+ dsoname = "[unknown]";
+
+ return fprintf(fp, "%s", dsoname);
+}
+
/*
* objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN.
* map->dso->adjust_symbols==1 for ET_EXEC-like cases.
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 2b8017f8a93..b100c20b7f9 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -118,6 +118,7 @@ void map__delete(struct map *self);
struct map *map__clone(struct map *self);
int map__overlap(struct map *l, struct map *r);
size_t map__fprintf(struct map *self, FILE *fp);
+size_t map__fprintf_dsoname(struct map *map, FILE *fp);
int map__load(struct map *self, symbol_filter_t filter);
struct symbol *map__find_symbol(struct map *self,
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index e33554a562b..8a8ee64e72d 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -34,7 +34,6 @@
#include "util.h"
#include "event.h"
-#include "string.h"
#include "strlist.h"
#include "debug.h"
#include "cache.h"
@@ -273,10 +272,10 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
/* Try to find perf_probe_event with debuginfo */
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs,
- int max_tevs, const char *module)
+ int max_tevs, const char *target)
{
bool need_dwarf = perf_probe_event_need_dwarf(pev);
- struct debuginfo *dinfo = open_debuginfo(module);
+ struct debuginfo *dinfo = open_debuginfo(target);
int ntevs, ret = 0;
if (!dinfo) {
@@ -295,9 +294,9 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
if (ntevs > 0) { /* Succeeded to find trace events */
pr_debug("find %d probe_trace_events.\n", ntevs);
- if (module)
+ if (target)
ret = add_module_to_probe_trace_events(*tevs, ntevs,
- module);
+ target);
return ret < 0 ? ret : ntevs;
}
@@ -1729,7 +1728,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
}
ret = 0;
- printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":");
+ printf("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
if (pev->event)
@@ -1784,7 +1783,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
if (ret >= 0) {
/* Show how to use the event. */
- printf("\nYou can now use it on all perf tools, such as:\n\n");
+ printf("\nYou can now use it in all perf tools, such as:\n\n");
printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
tev->event);
}
@@ -1796,14 +1795,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
static int convert_to_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs,
- int max_tevs, const char *module)
+ int max_tevs, const char *target)
{
struct symbol *sym;
int ret = 0, i;
struct probe_trace_event *tev;
/* Convert perf_probe_event with debuginfo */
- ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
+ ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
if (ret != 0)
return ret; /* Found in debuginfo or got an error */
@@ -1819,8 +1818,8 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
goto error;
}
- if (module) {
- tev->point.module = strdup(module);
+ if (target) {
+ tev->point.module = strdup(target);
if (tev->point.module == NULL) {
ret = -ENOMEM;
goto error;
@@ -1890,7 +1889,7 @@ struct __event_package {
};
int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- int max_tevs, const char *module, bool force_add)
+ int max_tevs, const char *target, bool force_add)
{
int i, j, ret;
struct __event_package *pkgs;
@@ -1913,7 +1912,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
ret = convert_to_probe_trace_events(pkgs[i].pev,
&pkgs[i].tevs,
max_tevs,
- module);
+ target);
if (ret < 0)
goto end;
pkgs[i].ntevs = ret;
@@ -1965,7 +1964,7 @@ static int __del_trace_probe_event(int fd, struct str_node *ent)
goto error;
}
- printf("Remove event: %s\n", ent->s);
+ printf("Removed event: %s\n", ent->s);
return 0;
error:
pr_warning("Failed to delete event: %s\n", strerror(-ret));
@@ -2069,7 +2068,7 @@ static int filter_available_functions(struct map *map __unused,
return 1;
}
-int show_available_funcs(const char *module, struct strfilter *_filter)
+int show_available_funcs(const char *target, struct strfilter *_filter)
{
struct map *map;
int ret;
@@ -2080,9 +2079,9 @@ int show_available_funcs(const char *module, struct strfilter *_filter)
if (ret < 0)
return ret;
- map = kernel_get_module_map(module);
+ map = kernel_get_module_map(target);
if (!map) {
- pr_err("Failed to find %s map.\n", (module) ? : "kernel");
+ pr_err("Failed to find %s map.\n", (target) ? : "kernel");
return -EINVAL;
}
available_func_filter = _filter;
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 74bd2e63c4b..2cc162d3b78 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -30,7 +30,6 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
-#include <ctype.h>
#include <dwarf-regs.h>
#include <linux/bitops.h>
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources
new file mode 100644
index 00000000000..2884e67ee62
--- /dev/null
+++ b/tools/perf/util/python-ext-sources
@@ -0,0 +1,19 @@
+#
+# List of files needed by perf python extention
+#
+# Each source file must be placed on its own line so that it can be
+# processed by Makefile and util/setup.py accordingly.
+#
+
+util/python.c
+util/ctype.c
+util/evlist.c
+util/evsel.c
+util/cpumap.c
+util/thread_map.c
+util/util.c
+util/xyarray.c
+util/cgroup.c
+util/debugfs.c
+util/strlist.c
+../../lib/rbtree.c
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 9dd47a4f259..e03b58a4842 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -425,14 +425,14 @@ struct pyrf_thread_map {
static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads,
PyObject *args, PyObject *kwargs)
{
- static char *kwlist[] = { "pid", "tid", NULL };
- int pid = -1, tid = -1;
+ static char *kwlist[] = { "pid", "tid", "uid", NULL };
+ int pid = -1, tid = -1, uid = UINT_MAX;
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii",
- kwlist, &pid, &tid))
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iii",
+ kwlist, &pid, &tid, &uid))
return -1;
- pthreads->threads = thread_map__new(pid, tid);
+ pthreads->threads = thread_map__new(pid, tid, uid);
if (pthreads->threads == NULL)
return -1;
return 0;
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 0b2a4878317..c2623c6f9b5 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -24,7 +24,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <ctype.h>
#include <errno.h>
#include "../../perf.h"
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index b5ca2558c7b..002ebbf59f4 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -24,7 +24,7 @@ static int perf_session__open(struct perf_session *self, bool force)
self->fd = STDIN_FILENO;
if (perf_session__read_header(self, self->fd) < 0)
- pr_err("incompatible file format");
+ pr_err("incompatible file format (rerun with -v to learn more)");
return 0;
}
@@ -56,7 +56,7 @@ static int perf_session__open(struct perf_session *self, bool force)
}
if (perf_session__read_header(self, self->fd) < 0) {
- pr_err("incompatible file format");
+ pr_err("incompatible file format (rerun with -v to learn more)");
goto out_close;
}
@@ -229,6 +229,64 @@ static bool symbol__match_parent_regex(struct symbol *sym)
return 0;
}
+static const u8 cpumodes[] = {
+ PERF_RECORD_MISC_USER,
+ PERF_RECORD_MISC_KERNEL,
+ PERF_RECORD_MISC_GUEST_USER,
+ PERF_RECORD_MISC_GUEST_KERNEL
+};
+#define NCPUMODES (sizeof(cpumodes)/sizeof(u8))
+
+static void ip__resolve_ams(struct machine *self, struct thread *thread,
+ struct addr_map_symbol *ams,
+ u64 ip)
+{
+ struct addr_location al;
+ size_t i;
+ u8 m;
+
+ memset(&al, 0, sizeof(al));
+
+ for (i = 0; i < NCPUMODES; i++) {
+ m = cpumodes[i];
+ /*
+ * We cannot use the header.misc hint to determine whether a
+ * branch stack address is user, kernel, guest, hypervisor.
+ * Branches may straddle the kernel/user/hypervisor boundaries.
+ * Thus, we have to try consecutively until we find a match
+ * or else, the symbol is unknown
+ */
+ thread__find_addr_location(thread, self, m, MAP__FUNCTION,
+ ip, &al, NULL);
+ if (al.sym)
+ goto found;
+ }
+found:
+ ams->addr = ip;
+ ams->al_addr = al.addr;
+ ams->sym = al.sym;
+ ams->map = al.map;
+}
+
+struct branch_info *machine__resolve_bstack(struct machine *self,
+ struct thread *thr,
+ struct branch_stack *bs)
+{
+ struct branch_info *bi;
+ unsigned int i;
+
+ bi = calloc(bs->nr, sizeof(struct branch_info));
+ if (!bi)
+ return NULL;
+
+ for (i = 0; i < bs->nr; i++) {
+ ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to);
+ ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from);
+ bi[i].flags = bs->entries[i].flags;
+ }
+ return bi;
+}
+
int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,
struct thread *thread,
struct ip_callchain *chain,
@@ -697,6 +755,18 @@ static void callchain__printf(struct perf_sample *sample)
i, sample->callchain->ips[i]);
}
+static void branch_stack__printf(struct perf_sample *sample)
+{
+ uint64_t i;
+
+ printf("... branch stack: nr:%" PRIu64 "\n", sample->branch_stack->nr);
+
+ for (i = 0; i < sample->branch_stack->nr; i++)
+ printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 "\n",
+ i, sample->branch_stack->entries[i].from,
+ sample->branch_stack->entries[i].to);
+}
+
static void perf_session__print_tstamp(struct perf_session *session,
union perf_event *event,
struct perf_sample *sample)
@@ -744,6 +814,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event,
if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
callchain__printf(sample);
+
+ if (session->sample_type & PERF_SAMPLE_BRANCH_STACK)
+ branch_stack__printf(sample);
}
static struct machine *
@@ -796,6 +869,10 @@ static int perf_session_deliver_event(struct perf_session *session,
++session->hists.stats.nr_unknown_id;
return -1;
}
+ if (machine == NULL) {
+ ++session->hists.stats.nr_unprocessable_samples;
+ return -1;
+ }
return tool->sample(tool, event, sample, evsel, machine);
case PERF_RECORD_MMAP:
return tool->mmap(tool, event, sample, machine);
@@ -964,6 +1041,12 @@ static void perf_session__warn_about_errors(const struct perf_session *session,
session->hists.stats.nr_invalid_chains,
session->hists.stats.nr_events[PERF_RECORD_SAMPLE]);
}
+
+ if (session->hists.stats.nr_unprocessable_samples != 0) {
+ ui__warning("%u unprocessable samples recorded.\n"
+ "Do you have a KVM guest running and not using 'perf kvm'?\n",
+ session->hists.stats.nr_unprocessable_samples);
+ }
}
#define session_done() (*(volatile int *)(&session_done))
@@ -1293,10 +1376,9 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
struct machine *machine, struct perf_evsel *evsel,
- int print_sym, int print_dso)
+ int print_sym, int print_dso, int print_symoffset)
{
struct addr_location al;
- const char *symname, *dsoname;
struct callchain_cursor *cursor = &evsel->hists.callchain_cursor;
struct callchain_cursor_node *node;
@@ -1324,20 +1406,13 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
printf("\t%16" PRIx64, node->ip);
if (print_sym) {
- if (node->sym && node->sym->name)
- symname = node->sym->name;
- else
- symname = "";
-
- printf(" %s", symname);
+ printf(" ");
+ symbol__fprintf_symname(node->sym, stdout);
}
if (print_dso) {
- if (node->map && node->map->dso && node->map->dso->name)
- dsoname = node->map->dso->name;
- else
- dsoname = "";
-
- printf(" (%s)", dsoname);
+ printf(" (");
+ map__fprintf_dsoname(al.map, stdout);
+ printf(")");
}
printf("\n");
@@ -1347,21 +1422,18 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
} else {
printf("%16" PRIx64, sample->ip);
if (print_sym) {
- if (al.sym && al.sym->name)
- symname = al.sym->name;
+ printf(" ");
+ if (print_symoffset)
+ symbol__fprintf_symname_offs(al.sym, &al,
+ stdout);
else
- symname = "";
-
- printf(" %s", symname);
+ symbol__fprintf_symname(al.sym, stdout);
}
if (print_dso) {
- if (al.map && al.map->dso && al.map->dso->name)
- dsoname = al.map->dso->name;
- else
- dsoname = "";
-
- printf(" (%s)", dsoname);
+ printf(" (");
+ map__fprintf_dsoname(al.map, stdout);
+ printf(")");
}
}
}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 37bc38381fb..7a5434c0056 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -73,6 +73,10 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel
struct ip_callchain *chain,
struct symbol **parent);
+struct branch_info *machine__resolve_bstack(struct machine *self,
+ struct thread *thread,
+ struct branch_stack *bs);
+
bool perf_session__has_traces(struct perf_session *self, const char *msg);
void mem_bswap_64(void *src, int byte_size);
@@ -147,7 +151,7 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
struct machine *machine, struct perf_evsel *evsel,
- int print_sym, int print_dso);
+ int print_sym, int print_dso, int print_symoffset);
int perf_session__cpu_bitmap(struct perf_session *session,
const char *cpu_list, unsigned long *cpu_bitmap);
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py
index 36d4c561957..d0f9f29cf18 100644
--- a/tools/perf/util/setup.py
+++ b/tools/perf/util/setup.py
@@ -24,11 +24,11 @@ cflags += getenv('CFLAGS', '').split()
build_lib = getenv('PYTHON_EXTBUILD_LIB')
build_tmp = getenv('PYTHON_EXTBUILD_TMP')
+ext_sources = [f.strip() for f in file('util/python-ext-sources')
+ if len(f.strip()) > 0 and f[0] != '#']
+
perf = Extension('perf',
- sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c',
- 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c',
- 'util/util.c', 'util/xyarray.c', 'util/cgroup.c',
- 'util/debugfs.c'],
+ sources = ext_sources,
include_dirs = ['util/include'],
extra_compile_args = cflags,
)
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 076c9d4e1ea..a27237430c5 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -8,6 +8,7 @@ const char default_sort_order[] = "comm,dso,symbol";
const char *sort_order = default_sort_order;
int sort__need_collapse = 0;
int sort__has_parent = 0;
+int sort__branch_mode = -1; /* -1 = means not set */
enum sort_type sort__first_dimension;
@@ -97,6 +98,26 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
}
+static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
+{
+ struct dso *dso_l = map_l ? map_l->dso : NULL;
+ struct dso *dso_r = map_r ? map_r->dso : NULL;
+ const char *dso_name_l, *dso_name_r;
+
+ if (!dso_l || !dso_r)
+ return cmp_null(dso_l, dso_r);
+
+ if (verbose) {
+ dso_name_l = dso_l->long_name;
+ dso_name_r = dso_r->long_name;
+ } else {
+ dso_name_l = dso_l->short_name;
+ dso_name_r = dso_r->short_name;
+ }
+
+ return strcmp(dso_name_l, dso_name_r);
+}
+
struct sort_entry sort_comm = {
.se_header = "Command",
.se_cmp = sort__comm_cmp,
@@ -110,36 +131,74 @@ struct sort_entry sort_comm = {
static int64_t
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
{
- struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
- struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL;
- const char *dso_name_l, *dso_name_r;
+ return _sort__dso_cmp(left->ms.map, right->ms.map);
+}
- if (!dso_l || !dso_r)
- return cmp_null(dso_l, dso_r);
- if (verbose) {
- dso_name_l = dso_l->long_name;
- dso_name_r = dso_r->long_name;
- } else {
- dso_name_l = dso_l->short_name;
- dso_name_r = dso_r->short_name;
+static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
+ u64 ip_l, u64 ip_r)
+{
+ if (!sym_l || !sym_r)
+ return cmp_null(sym_l, sym_r);
+
+ if (sym_l == sym_r)
+ return 0;
+
+ if (sym_l)
+ ip_l = sym_l->start;
+ if (sym_r)
+ ip_r = sym_r->start;
+
+ return (int64_t)(ip_r - ip_l);
+}
+
+static int _hist_entry__dso_snprintf(struct map *map, char *bf,
+ size_t size, unsigned int width)
+{
+ if (map && map->dso) {
+ const char *dso_name = !verbose ? map->dso->short_name :
+ map->dso->long_name;
+ return repsep_snprintf(bf, size, "%-*s", width, dso_name);
}
- return strcmp(dso_name_l, dso_name_r);
+ return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
}
static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
size_t size, unsigned int width)
{
- if (self->ms.map && self->ms.map->dso) {
- const char *dso_name = !verbose ? self->ms.map->dso->short_name :
- self->ms.map->dso->long_name;
- return repsep_snprintf(bf, size, "%-*s", width, dso_name);
+ return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
+}
+
+static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
+ u64 ip, char level, char *bf, size_t size,
+ unsigned int width __used)
+{
+ size_t ret = 0;
+
+ if (verbose) {
+ char o = map ? dso__symtab_origin(map->dso) : '!';
+ ret += repsep_snprintf(bf, size, "%-#*llx %c ",
+ BITS_PER_LONG / 4, ip, o);
}
- return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
+ ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
+ if (sym)
+ ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
+ width - ret,
+ sym->name);
+ else {
+ size_t len = BITS_PER_LONG / 4;
+ ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
+ len, ip);
+ ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
+ width - ret, "");
+ }
+
+ return ret;
}
+
struct sort_entry sort_dso = {
.se_header = "Shared Object",
.se_cmp = sort__dso_cmp,
@@ -147,8 +206,14 @@ struct sort_entry sort_dso = {
.se_width_idx = HISTC_DSO,
};
-/* --sort symbol */
+static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width __used)
+{
+ return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
+ self->level, bf, size, width);
+}
+/* --sort symbol */
static int64_t
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
{
@@ -166,31 +231,7 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
ip_l = left->ms.sym->start;
ip_r = right->ms.sym->start;
- return (int64_t)(ip_r - ip_l);
-}
-
-static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width __used)
-{
- size_t ret = 0;
-
- if (verbose) {
- char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
- ret += repsep_snprintf(bf, size, "%-#*llx %c ",
- BITS_PER_LONG / 4, self->ip, o);
- }
-
- if (!sort_dso.elide)
- ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
-
- if (self->ms.sym)
- ret += repsep_snprintf(bf + ret, size - ret, "%s",
- self->ms.sym->name);
- else
- ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx",
- BITS_PER_LONG / 4, self->ip);
-
- return ret;
+ return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r);
}
struct sort_entry sort_sym = {
@@ -249,19 +290,155 @@ struct sort_entry sort_cpu = {
.se_width_idx = HISTC_CPU,
};
+static int64_t
+sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return _sort__dso_cmp(left->branch_info->from.map,
+ right->branch_info->from.map);
+}
+
+static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return _hist_entry__dso_snprintf(self->branch_info->from.map,
+ bf, size, width);
+}
+
+struct sort_entry sort_dso_from = {
+ .se_header = "Source Shared Object",
+ .se_cmp = sort__dso_from_cmp,
+ .se_snprintf = hist_entry__dso_from_snprintf,
+ .se_width_idx = HISTC_DSO_FROM,
+};
+
+static int64_t
+sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return _sort__dso_cmp(left->branch_info->to.map,
+ right->branch_info->to.map);
+}
+
+static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width)
+{
+ return _hist_entry__dso_snprintf(self->branch_info->to.map,
+ bf, size, width);
+}
+
+static int64_t
+sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct addr_map_symbol *from_l = &left->branch_info->from;
+ struct addr_map_symbol *from_r = &right->branch_info->from;
+
+ if (!from_l->sym && !from_r->sym)
+ return right->level - left->level;
+
+ return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr,
+ from_r->addr);
+}
+
+static int64_t
+sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct addr_map_symbol *to_l = &left->branch_info->to;
+ struct addr_map_symbol *to_r = &right->branch_info->to;
+
+ if (!to_l->sym && !to_r->sym)
+ return right->level - left->level;
+
+ return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr);
+}
+
+static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width __used)
+{
+ struct addr_map_symbol *from = &self->branch_info->from;
+ return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
+ self->level, bf, size, width);
+
+}
+
+static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width __used)
+{
+ struct addr_map_symbol *to = &self->branch_info->to;
+ return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
+ self->level, bf, size, width);
+
+}
+
+struct sort_entry sort_dso_to = {
+ .se_header = "Target Shared Object",
+ .se_cmp = sort__dso_to_cmp,
+ .se_snprintf = hist_entry__dso_to_snprintf,
+ .se_width_idx = HISTC_DSO_TO,
+};
+
+struct sort_entry sort_sym_from = {
+ .se_header = "Source Symbol",
+ .se_cmp = sort__sym_from_cmp,
+ .se_snprintf = hist_entry__sym_from_snprintf,
+ .se_width_idx = HISTC_SYMBOL_FROM,
+};
+
+struct sort_entry sort_sym_to = {
+ .se_header = "Target Symbol",
+ .se_cmp = sort__sym_to_cmp,
+ .se_snprintf = hist_entry__sym_to_snprintf,
+ .se_width_idx = HISTC_SYMBOL_TO,
+};
+
+static int64_t
+sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ const unsigned char mp = left->branch_info->flags.mispred !=
+ right->branch_info->flags.mispred;
+ const unsigned char p = left->branch_info->flags.predicted !=
+ right->branch_info->flags.predicted;
+
+ return mp || p;
+}
+
+static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
+ size_t size, unsigned int width){
+ static const char *out = "N/A";
+
+ if (self->branch_info->flags.predicted)
+ out = "N";
+ else if (self->branch_info->flags.mispred)
+ out = "Y";
+
+ return repsep_snprintf(bf, size, "%-*s", width, out);
+}
+
+struct sort_entry sort_mispredict = {
+ .se_header = "Branch Mispredicted",
+ .se_cmp = sort__mispredict_cmp,
+ .se_snprintf = hist_entry__mispredict_snprintf,
+ .se_width_idx = HISTC_MISPREDICT,
+};
+
struct sort_dimension {
const char *name;
struct sort_entry *entry;
int taken;
};
+#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
+
static struct sort_dimension sort_dimensions[] = {
- { .name = "pid", .entry = &sort_thread, },
- { .name = "comm", .entry = &sort_comm, },
- { .name = "dso", .entry = &sort_dso, },
- { .name = "symbol", .entry = &sort_sym, },
- { .name = "parent", .entry = &sort_parent, },
- { .name = "cpu", .entry = &sort_cpu, },
+ DIM(SORT_PID, "pid", sort_thread),
+ DIM(SORT_COMM, "comm", sort_comm),
+ DIM(SORT_DSO, "dso", sort_dso),
+ DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
+ DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
+ DIM(SORT_SYM, "symbol", sort_sym),
+ DIM(SORT_SYM_TO, "symbol_from", sort_sym_from),
+ DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to),
+ DIM(SORT_PARENT, "parent", sort_parent),
+ DIM(SORT_CPU, "cpu", sort_cpu),
+ DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
};
int sort_dimension__add(const char *tok)
@@ -273,7 +450,6 @@ int sort_dimension__add(const char *tok)
if (strncasecmp(tok, sd->name, strlen(tok)))
continue;
-
if (sd->entry == &sort_parent) {
int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
if (ret) {
@@ -305,6 +481,16 @@ int sort_dimension__add(const char *tok)
sort__first_dimension = SORT_PARENT;
else if (!strcmp(sd->name, "cpu"))
sort__first_dimension = SORT_CPU;
+ else if (!strcmp(sd->name, "symbol_from"))
+ sort__first_dimension = SORT_SYM_FROM;
+ else if (!strcmp(sd->name, "symbol_to"))
+ sort__first_dimension = SORT_SYM_TO;
+ else if (!strcmp(sd->name, "dso_from"))
+ sort__first_dimension = SORT_DSO_FROM;
+ else if (!strcmp(sd->name, "dso_to"))
+ sort__first_dimension = SORT_DSO_TO;
+ else if (!strcmp(sd->name, "mispredict"))
+ sort__first_dimension = SORT_MISPREDICT;
}
list_add_tail(&sd->entry->list, &hist_entry__sort_list);
@@ -312,7 +498,6 @@ int sort_dimension__add(const char *tok)
return 0;
}
-
return -ESRCH;
}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 3f67ae39575..472aa5a63a5 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -31,11 +31,16 @@ extern const char *parent_pattern;
extern const char default_sort_order[];
extern int sort__need_collapse;
extern int sort__has_parent;
+extern int sort__branch_mode;
extern char *field_sep;
extern struct sort_entry sort_comm;
extern struct sort_entry sort_dso;
extern struct sort_entry sort_sym;
extern struct sort_entry sort_parent;
+extern struct sort_entry sort_dso_from;
+extern struct sort_entry sort_dso_to;
+extern struct sort_entry sort_sym_from;
+extern struct sort_entry sort_sym_to;
extern enum sort_type sort__first_dimension;
/**
@@ -72,6 +77,7 @@ struct hist_entry {
struct hist_entry *pair;
struct rb_root sorted_chain;
};
+ struct branch_info *branch_info;
struct callchain_root callchain[0];
};
@@ -82,6 +88,11 @@ enum sort_type {
SORT_SYM,
SORT_PARENT,
SORT_CPU,
+ SORT_DSO_FROM,
+ SORT_DSO_TO,
+ SORT_SYM_FROM,
+ SORT_SYM_TO,
+ SORT_MISPREDICT,
};
/*
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 0975438c3e7..5dd83c3e2c0 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1,4 +1,3 @@
-#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <libgen.h>
@@ -12,6 +11,7 @@
#include <unistd.h>
#include <inttypes.h>
#include "build-id.h"
+#include "util.h"
#include "debug.h"
#include "symbol.h"
#include "strlist.h"
@@ -263,6 +263,28 @@ static size_t symbol__fprintf(struct symbol *sym, FILE *fp)
sym->name);
}
+size_t symbol__fprintf_symname_offs(const struct symbol *sym,
+ const struct addr_location *al, FILE *fp)
+{
+ unsigned long offset;
+ size_t length;
+
+ if (sym && sym->name) {
+ length = fprintf(fp, "%s", sym->name);
+ if (al) {
+ offset = al->addr - sym->start;
+ length += fprintf(fp, "+0x%lx", offset);
+ }
+ return length;
+ } else
+ return fprintf(fp, "[unknown]");
+}
+
+size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp)
+{
+ return symbol__fprintf_symname_offs(sym, NULL, fp);
+}
+
void dso__set_long_name(struct dso *dso, char *name)
{
if (name == NULL)
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 123c2e14353..ac49ef208a5 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -5,6 +5,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "map.h"
+#include "../perf.h"
#include <linux/list.h>
#include <linux/rbtree.h>
#include <stdio.h>
@@ -70,6 +71,7 @@ struct symbol_conf {
unsigned short priv_size;
unsigned short nr_events;
bool try_vmlinux_path,
+ show_kernel_path,
use_modules,
sort_by_name,
show_nr_samples,
@@ -95,7 +97,11 @@ struct symbol_conf {
*col_width_list_str;
struct strlist *dso_list,
*comm_list,
- *sym_list;
+ *sym_list,
+ *dso_from_list,
+ *dso_to_list,
+ *sym_from_list,
+ *sym_to_list;
const char *symfs;
};
@@ -119,6 +125,19 @@ struct map_symbol {
bool has_children;
};
+struct addr_map_symbol {
+ struct map *map;
+ struct symbol *sym;
+ u64 addr;
+ u64 al_addr;
+};
+
+struct branch_info {
+ struct addr_map_symbol from;
+ struct addr_map_symbol to;
+ struct branch_flags flags;
+};
+
struct addr_location {
struct thread *thread;
struct map *map;
@@ -241,6 +260,9 @@ void machines__destroy_guest_kernel_maps(struct rb_root *machines);
int symbol__init(void);
void symbol__exit(void);
+size_t symbol__fprintf_symname_offs(const struct symbol *sym,
+ const struct addr_location *al, FILE *fp);
+size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp);
bool symbol_type__is_a(char symbol_type, enum map_type map_type);
size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp);
diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c
new file mode 100644
index 00000000000..48c6902e749
--- /dev/null
+++ b/tools/perf/util/sysfs.c
@@ -0,0 +1,60 @@
+
+#include "util.h"
+#include "sysfs.h"
+
+static const char * const sysfs_known_mountpoints[] = {
+ "/sys",
+ 0,
+};
+
+static int sysfs_found;
+char sysfs_mountpoint[PATH_MAX];
+
+static int sysfs_valid_mountpoint(const char *sysfs)
+{
+ struct statfs st_fs;
+
+ if (statfs(sysfs, &st_fs) < 0)
+ return -ENOENT;
+ else if (st_fs.f_type != (long) SYSFS_MAGIC)
+ return -ENOENT;
+
+ return 0;
+}
+
+const char *sysfs_find_mountpoint(void)
+{
+ const char * const *ptr;
+ char type[100];
+ FILE *fp;
+
+ if (sysfs_found)
+ return (const char *) sysfs_mountpoint;
+
+ ptr = sysfs_known_mountpoints;
+ while (*ptr) {
+ if (sysfs_valid_mountpoint(*ptr) == 0) {
+ sysfs_found = 1;
+ strcpy(sysfs_mountpoint, *ptr);
+ return sysfs_mountpoint;
+ }
+ ptr++;
+ }
+
+ /* give up and parse /proc/mounts */
+ fp = fopen("/proc/mounts", "r");
+ if (fp == NULL)
+ return NULL;
+
+ while (!sysfs_found &&
+ fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n",
+ sysfs_mountpoint, type) == 2) {
+
+ if (strcmp(type, "sysfs") == 0)
+ sysfs_found = 1;
+ }
+
+ fclose(fp);
+
+ return sysfs_found ? sysfs_mountpoint : NULL;
+}
diff --git a/tools/perf/util/sysfs.h b/tools/perf/util/sysfs.h
new file mode 100644
index 00000000000..a813b720393
--- /dev/null
+++ b/tools/perf/util/sysfs.h
@@ -0,0 +1,6 @@
+#ifndef __SYSFS_H__
+#define __SYSFS_H__
+
+const char *sysfs_find_mountpoint(void);
+
+#endif /* __DEBUGFS_H__ */
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index a5df131b77c..84d9bd78200 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -1,6 +1,13 @@
#include <dirent.h>
+#include <limits.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "strlist.h"
+#include <string.h>
#include "thread_map.h"
/* Skip "." and ".." directories */
@@ -23,7 +30,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)
sprintf(name, "/proc/%d/task", pid);
items = scandir(name, &namelist, filter, NULL);
if (items <= 0)
- return NULL;
+ return NULL;
threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
if (threads != NULL) {
@@ -51,14 +58,240 @@ struct thread_map *thread_map__new_by_tid(pid_t tid)
return threads;
}
-struct thread_map *thread_map__new(pid_t pid, pid_t tid)
+struct thread_map *thread_map__new_by_uid(uid_t uid)
+{
+ DIR *proc;
+ int max_threads = 32, items, i;
+ char path[256];
+ struct dirent dirent, *next, **namelist = NULL;
+ struct thread_map *threads = malloc(sizeof(*threads) +
+ max_threads * sizeof(pid_t));
+ if (threads == NULL)
+ goto out;
+
+ proc = opendir("/proc");
+ if (proc == NULL)
+ goto out_free_threads;
+
+ threads->nr = 0;
+
+ while (!readdir_r(proc, &dirent, &next) && next) {
+ char *end;
+ bool grow = false;
+ struct stat st;
+ pid_t pid = strtol(dirent.d_name, &end, 10);
+
+ if (*end) /* only interested in proper numerical dirents */
+ continue;
+
+ snprintf(path, sizeof(path), "/proc/%s", dirent.d_name);
+
+ if (stat(path, &st) != 0)
+ continue;
+
+ if (st.st_uid != uid)
+ continue;
+
+ snprintf(path, sizeof(path), "/proc/%d/task", pid);
+ items = scandir(path, &namelist, filter, NULL);
+ if (items <= 0)
+ goto out_free_closedir;
+
+ while (threads->nr + items >= max_threads) {
+ max_threads *= 2;
+ grow = true;
+ }
+
+ if (grow) {
+ struct thread_map *tmp;
+
+ tmp = realloc(threads, (sizeof(*threads) +
+ max_threads * sizeof(pid_t)));
+ if (tmp == NULL)
+ goto out_free_namelist;
+
+ threads = tmp;
+ }
+
+ for (i = 0; i < items; i++)
+ threads->map[threads->nr + i] = atoi(namelist[i]->d_name);
+
+ for (i = 0; i < items; i++)
+ free(namelist[i]);
+ free(namelist);
+
+ threads->nr += items;
+ }
+
+out_closedir:
+ closedir(proc);
+out:
+ return threads;
+
+out_free_threads:
+ free(threads);
+ return NULL;
+
+out_free_namelist:
+ for (i = 0; i < items; i++)
+ free(namelist[i]);
+ free(namelist);
+
+out_free_closedir:
+ free(threads);
+ threads = NULL;
+ goto out_closedir;
+}
+
+struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
{
if (pid != -1)
return thread_map__new_by_pid(pid);
+
+ if (tid == -1 && uid != UINT_MAX)
+ return thread_map__new_by_uid(uid);
+
return thread_map__new_by_tid(tid);
}
+static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
+{
+ struct thread_map *threads = NULL, *nt;
+ char name[256];
+ int items, total_tasks = 0;
+ struct dirent **namelist = NULL;
+ int i, j = 0;
+ pid_t pid, prev_pid = INT_MAX;
+ char *end_ptr;
+ struct str_node *pos;
+ struct strlist *slist = strlist__new(false, pid_str);
+
+ if (!slist)
+ return NULL;
+
+ strlist__for_each(pos, slist) {
+ pid = strtol(pos->s, &end_ptr, 10);
+
+ if (pid == INT_MIN || pid == INT_MAX ||
+ (*end_ptr != '\0' && *end_ptr != ','))
+ goto out_free_threads;
+
+ if (pid == prev_pid)
+ continue;
+
+ sprintf(name, "/proc/%d/task", pid);
+ items = scandir(name, &namelist, filter, NULL);
+ if (items <= 0)
+ goto out_free_threads;
+
+ total_tasks += items;
+ nt = realloc(threads, (sizeof(*threads) +
+ sizeof(pid_t) * total_tasks));
+ if (nt == NULL)
+ goto out_free_threads;
+
+ threads = nt;
+
+ if (threads) {
+ for (i = 0; i < items; i++)
+ threads->map[j++] = atoi(namelist[i]->d_name);
+ threads->nr = total_tasks;
+ }
+
+ for (i = 0; i < items; i++)
+ free(namelist[i]);
+ free(namelist);
+
+ if (!threads)
+ break;
+ }
+
+out:
+ strlist__delete(slist);
+ return threads;
+
+out_free_threads:
+ free(threads);
+ threads = NULL;
+ goto out;
+}
+
+static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
+{
+ struct thread_map *threads = NULL, *nt;
+ int ntasks = 0;
+ pid_t tid, prev_tid = INT_MAX;
+ char *end_ptr;
+ struct str_node *pos;
+ struct strlist *slist;
+
+ /* perf-stat expects threads to be generated even if tid not given */
+ if (!tid_str) {
+ threads = malloc(sizeof(*threads) + sizeof(pid_t));
+ if (threads != NULL) {
+ threads->map[0] = -1;
+ threads->nr = 1;
+ }
+ return threads;
+ }
+
+ slist = strlist__new(false, tid_str);
+ if (!slist)
+ return NULL;
+
+ strlist__for_each(pos, slist) {
+ tid = strtol(pos->s, &end_ptr, 10);
+
+ if (tid == INT_MIN || tid == INT_MAX ||
+ (*end_ptr != '\0' && *end_ptr != ','))
+ goto out_free_threads;
+
+ if (tid == prev_tid)
+ continue;
+
+ ntasks++;
+ nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks);
+
+ if (nt == NULL)
+ goto out_free_threads;
+
+ threads = nt;
+ threads->map[ntasks - 1] = tid;
+ threads->nr = ntasks;
+ }
+out:
+ return threads;
+
+out_free_threads:
+ free(threads);
+ threads = NULL;
+ goto out;
+}
+
+struct thread_map *thread_map__new_str(const char *pid, const char *tid,
+ uid_t uid)
+{
+ if (pid)
+ return thread_map__new_by_pid_str(pid);
+
+ if (!tid && uid != UINT_MAX)
+ return thread_map__new_by_uid(uid);
+
+ return thread_map__new_by_tid_str(tid);
+}
+
void thread_map__delete(struct thread_map *threads)
{
free(threads);
}
+
+size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
+{
+ int i;
+ size_t printed = fprintf(fp, "%d thread%s: ",
+ threads->nr, threads->nr > 1 ? "s" : "");
+ for (i = 0; i < threads->nr; ++i)
+ printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]);
+
+ return printed + fprintf(fp, "\n");
+}
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h
index 3cb90731140..7da80f14418 100644
--- a/tools/perf/util/thread_map.h
+++ b/tools/perf/util/thread_map.h
@@ -2,6 +2,7 @@
#define __PERF_THREAD_MAP_H
#include <sys/types.h>
+#include <stdio.h>
struct thread_map {
int nr;
@@ -10,6 +11,14 @@ struct thread_map {
struct thread_map *thread_map__new_by_pid(pid_t pid);
struct thread_map *thread_map__new_by_tid(pid_t tid);
-struct thread_map *thread_map__new(pid_t pid, pid_t tid);
+struct thread_map *thread_map__new_by_uid(uid_t uid);
+struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid);
+
+struct thread_map *thread_map__new_str(const char *pid,
+ const char *tid, uid_t uid);
+
void thread_map__delete(struct thread_map *threads);
+
+size_t thread_map__fprintf(struct thread_map *threads, FILE *fp);
+
#endif /* __PERF_THREAD_MAP_H */
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c
index 500471dffa4..09fe579ccaf 100644
--- a/tools/perf/util/top.c
+++ b/tools/perf/util/top.c
@@ -69,12 +69,15 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
ret += SNPRINTF(bf + ret, size - ret, "], ");
- if (top->target_pid != -1)
- ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %d",
+ if (top->target_pid)
+ ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s",
top->target_pid);
- else if (top->target_tid != -1)
- ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d",
+ else if (top->target_tid)
+ ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s",
top->target_tid);
+ else if (top->uid_str != NULL)
+ ret += SNPRINTF(bf + ret, size - ret, " (uid: %s",
+ top->uid_str);
else
ret += SNPRINTF(bf + ret, size - ret, " (all");
@@ -82,7 +85,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)",
top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list);
else {
- if (top->target_tid != -1)
+ if (top->target_tid)
ret += SNPRINTF(bf + ret, size - ret, ")");
else
ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)",
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h
index f2eab81435a..ce61cb2d1ac 100644
--- a/tools/perf/util/top.h
+++ b/tools/perf/util/top.h
@@ -23,7 +23,8 @@ struct perf_top {
u64 guest_us_samples, guest_kernel_samples;
int print_entries, count_filter, delay_secs;
int freq;
- pid_t target_pid, target_tid;
+ const char *target_pid, *target_tid;
+ uid_t uid;
bool hide_kernel_symbols, hide_user_symbols, zero;
bool system_wide;
bool use_tui, use_stdio;
@@ -33,7 +34,7 @@ struct perf_top {
bool vmlinux_warned;
bool inherit;
bool group;
- bool sample_id_all_avail;
+ bool sample_id_all_missing;
bool exclude_guest_missing;
bool dump_symtab;
const char *cpu_list;
@@ -46,6 +47,7 @@ struct perf_top {
int realtime_prio;
int sym_pcnt_filter;
const char *sym_filter;
+ const char *uid_str;
};
size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size);
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index 1a8d4dc4f38..a4088ced1e6 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -25,7 +25,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <ctype.h>
#include <errno.h>
#include "../perf.h"
@@ -1424,6 +1423,11 @@ static long long arg_num_eval(struct print_arg *arg)
die("unknown op '%s'", arg->op.op);
}
break;
+ case '+':
+ left = arg_num_eval(arg->op.left);
+ right = arg_num_eval(arg->op.right);
+ val = left + right;
+ break;
default:
die("unknown op '%s'", arg->op.op);
}
@@ -1484,6 +1488,13 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok)
free_token(token);
type = process_arg(event, arg, &token);
+
+ if (type == EVENT_OP)
+ type = process_op(event, arg, &token);
+
+ if (type == EVENT_ERROR)
+ goto out_free;
+
if (test_type_token(type, token, EVENT_DELIM, ","))
goto out_free;
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index f55cc3a765a..b9592e0de8d 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -33,7 +33,6 @@
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
-#include <ctype.h>
#include <errno.h>
#include "../perf.h"
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index a3fdf55f317..18ae6c1831d 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -22,7 +22,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <ctype.h>
#include <errno.h>
#include "../perf.h"
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
index 295a9c93f94..57a4c6ef3fd 100644
--- a/tools/perf/util/ui/browsers/annotate.c
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -69,14 +69,17 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
if (!self->navkeypressed)
width += 1;
+ if (!ab->hide_src_code && ol->offset != -1)
+ if (!current_entry || (self->use_navkeypressed &&
+ !self->navkeypressed))
+ ui_browser__set_color(self, HE_COLORSET_CODE);
+
if (!*ol->line)
slsmg_write_nstring(" ", width - 18);
else
slsmg_write_nstring(ol->line, width - 18);
- if (!current_entry)
- ui_browser__set_color(self, HE_COLORSET_CODE);
- else
+ if (current_entry)
ab->selection = ol;
}
@@ -230,9 +233,9 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
struct rb_node *nd = NULL;
struct map_symbol *ms = self->b.priv;
struct symbol *sym = ms->sym;
- const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, "
- "H: Hottest, -> Line action, S -> Toggle source "
- "code view";
+ const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
+ "H: Go to hottest line, ->/ENTER: Line action, "
+ "S: Toggle source code view";
int key;
if (ui_browser__show(&self->b, sym->name, help) < 0)
@@ -284,9 +287,11 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
nd = self->curr_hot;
break;
case 'H':
+ case 'h':
nd = self->curr_hot;
break;
case 'S':
+ case 's':
if (annotate_browser__toggle_source(self))
ui_helpline__puts(help);
continue;
@@ -338,6 +343,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
pthread_mutex_unlock(&notes->lock);
symbol__tui_annotate(target, ms->map, evidx,
timer, arg, delay_secs);
+ ui_browser__show_title(&self->b, sym->name);
}
continue;
case K_LEFT:
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
index bb9197c9c4a..fa530fcc764 100644
--- a/tools/perf/util/ui/browsers/hists.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -805,8 +805,11 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
self->hists = hists;
self->b.refresh = hist_browser__refresh;
self->b.seek = ui_browser__hists_seek;
- self->b.use_navkeypressed = true,
- self->has_symbols = sort_sym.list.next != NULL;
+ self->b.use_navkeypressed = true;
+ if (sort__branch_mode == 1)
+ self->has_symbols = sort_sym_from.list.next != NULL;
+ else
+ self->has_symbols = sort_sym.list.next != NULL;
}
return self;
@@ -839,6 +842,9 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,
nr_events = convert_unit(nr_events, &unit);
printed = scnprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
+ if (self->uid_filter_str)
+ printed += snprintf(bf + printed, size - printed,
+ ", UID: %s", self->uid_filter_str);
if (thread)
printed += scnprintf(bf + printed, size - printed,
", Thread: %s(%d)",
@@ -850,6 +856,16 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,
return printed;
}
+static inline void free_popup_options(char **options, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ free(options[i]);
+ options[i] = NULL;
+ }
+}
+
static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
const char *helpline, const char *ev_name,
bool left_exits,
@@ -858,7 +874,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
{
struct hists *self = &evsel->hists;
struct hist_browser *browser = hist_browser__new(self);
+ struct branch_info *bi;
struct pstack *fstack;
+ char *options[16];
+ int nr_options = 0;
int key = -1;
if (browser == NULL)
@@ -870,13 +889,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
ui_helpline__push(helpline);
+ memset(options, 0, sizeof(options));
+
while (1) {
const struct thread *thread = NULL;
const struct dso *dso = NULL;
- char *options[16];
- int nr_options = 0, choice = 0, i,
+ int choice = 0,
annotate = -2, zoom_dso = -2, zoom_thread = -2,
- browse_map = -2;
+ annotate_f = -2, annotate_t = -2, browse_map = -2;
+
+ nr_options = 0;
key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
@@ -884,7 +906,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
thread = hist_browser__selected_thread(browser);
dso = browser->selection->map ? browser->selection->map->dso : NULL;
}
-
switch (key) {
case K_TAB:
case K_UNTAB:
@@ -899,7 +920,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
if (!browser->has_symbols) {
ui_browser__warning(&browser->b, delay_secs * 2,
"Annotation is only available for symbolic views, "
- "include \"sym\" in --sort to use it.");
+ "include \"sym*\" in --sort to use it.");
continue;
}
@@ -969,12 +990,34 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
if (!browser->has_symbols)
goto add_exit_option;
- if (browser->selection != NULL &&
- browser->selection->sym != NULL &&
- !browser->selection->map->dso->annotate_warned &&
- asprintf(&options[nr_options], "Annotate %s",
- browser->selection->sym->name) > 0)
- annotate = nr_options++;
+ if (sort__branch_mode == 1) {
+ bi = browser->he_selection->branch_info;
+ if (browser->selection != NULL &&
+ bi &&
+ bi->from.sym != NULL &&
+ !bi->from.map->dso->annotate_warned &&
+ asprintf(&options[nr_options], "Annotate %s",
+ bi->from.sym->name) > 0)
+ annotate_f = nr_options++;
+
+ if (browser->selection != NULL &&
+ bi &&
+ bi->to.sym != NULL &&
+ !bi->to.map->dso->annotate_warned &&
+ (bi->to.sym != bi->from.sym ||
+ bi->to.map->dso != bi->from.map->dso) &&
+ asprintf(&options[nr_options], "Annotate %s",
+ bi->to.sym->name) > 0)
+ annotate_t = nr_options++;
+ } else {
+
+ if (browser->selection != NULL &&
+ browser->selection->sym != NULL &&
+ !browser->selection->map->dso->annotate_warned &&
+ asprintf(&options[nr_options], "Annotate %s",
+ browser->selection->sym->name) > 0)
+ annotate = nr_options++;
+ }
if (thread != NULL &&
asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
@@ -995,25 +1038,39 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
browse_map = nr_options++;
add_exit_option:
options[nr_options++] = (char *)"Exit";
-
+retry_popup_menu:
choice = ui__popup_menu(nr_options, options);
- for (i = 0; i < nr_options - 1; ++i)
- free(options[i]);
-
if (choice == nr_options - 1)
break;
- if (choice == -1)
+ if (choice == -1) {
+ free_popup_options(options, nr_options - 1);
continue;
+ }
- if (choice == annotate) {
+ if (choice == annotate || choice == annotate_t || choice == annotate_f) {
struct hist_entry *he;
int err;
do_annotate:
he = hist_browser__selected_entry(browser);
if (he == NULL)
continue;
+
+ /*
+ * we stash the branch_info symbol + map into the
+ * the ms so we don't have to rewrite all the annotation
+ * code to use branch_info.
+ * in branch mode, the ms struct is not used
+ */
+ if (choice == annotate_f) {
+ he->ms.sym = he->branch_info->from.sym;
+ he->ms.map = he->branch_info->from.map;
+ } else if (choice == annotate_t) {
+ he->ms.sym = he->branch_info->to.sym;
+ he->ms.map = he->branch_info->to.map;
+ }
+
/*
* Don't let this be freed, say, by hists__decay_entry.
*/
@@ -1021,9 +1078,18 @@ do_annotate:
err = hist_entry__tui_annotate(he, evsel->idx,
timer, arg, delay_secs);
he->used = false;
+ /*
+ * offer option to annotate the other branch source or target
+ * (if they exists) when returning from annotate
+ */
+ if ((err == 'q' || err == CTRL('c'))
+ && annotate_t != -2 && annotate_f != -2)
+ goto retry_popup_menu;
+
ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
if (err)
ui_browser__handle_resize(&browser->b);
+
} else if (choice == browse_map)
map__browse(browser->selection->map);
else if (choice == zoom_dso) {
@@ -1069,6 +1135,7 @@ out_free_stack:
pstack__delete(fstack);
out:
hist_browser__delete(browser);
+ free_popup_options(options, nr_options - 1);
return key;
}
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
index 6905bcc8be2..eca6575abfd 100644
--- a/tools/perf/util/ui/browsers/map.c
+++ b/tools/perf/util/ui/browsers/map.c
@@ -3,9 +3,9 @@
#include <newt.h>
#include <inttypes.h>
#include <sys/ttydefaults.h>
-#include <ctype.h>
#include <string.h>
#include <linux/bitops.h>
+#include "../../util.h"
#include "../../debug.h"
#include "../../symbol.h"
#include "../browser.h"
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c
index d76d1c0ff98..52bb07c6442 100644
--- a/tools/perf/util/usage.c
+++ b/tools/perf/util/usage.c
@@ -7,6 +7,7 @@
* Copyright (C) Linus Torvalds, 2005
*/
#include "util.h"
+#include "debug.h"
static void report(const char *prefix, const char *err, va_list params)
{
@@ -81,3 +82,41 @@ void warning(const char *warn, ...)
warn_routine(warn, params);
va_end(params);
}
+
+uid_t parse_target_uid(const char *str, const char *tid, const char *pid)
+{
+ struct passwd pwd, *result;
+ char buf[1024];
+
+ if (str == NULL)
+ return UINT_MAX;
+
+ /* UID and PID are mutually exclusive */
+ if (tid || pid) {
+ ui__warning("PID/TID switch overriding UID\n");
+ sleep(1);
+ return UINT_MAX;
+ }
+
+ getpwnam_r(str, &pwd, buf, sizeof(buf), &result);
+
+ if (result == NULL) {
+ char *endptr;
+ int uid = strtol(str, &endptr, 10);
+
+ if (*endptr != '\0') {
+ ui__error("Invalid user %s\n", str);
+ return UINT_MAX - 1;
+ }
+
+ getpwuid_r(uid, &pwd, buf, sizeof(buf), &result);
+
+ if (result == NULL) {
+ ui__error("Problems obtaining information for user %s\n",
+ str);
+ return UINT_MAX - 1;
+ }
+ }
+
+ return result->pw_uid;
+}
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index fb25d132921..8109a907841 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -14,6 +14,8 @@ void event_attr_init(struct perf_event_attr *attr)
attr->exclude_host = 1;
if (!perf_guest)
attr->exclude_guest = 1;
+ /* to capture ABI version */
+ attr->size = sizeof(*attr);
}
int mkdir_p(char *path, mode_t mode)
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index ecf9898169c..0f99f394d8e 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -199,6 +199,8 @@ static inline int has_extension(const char *filename, const char *ext)
#undef isalpha
#undef isprint
#undef isalnum
+#undef islower
+#undef isupper
#undef tolower
#undef toupper
@@ -219,6 +221,8 @@ extern unsigned char sane_ctype[256];
#define isalpha(x) sane_istest(x,GIT_ALPHA)
#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
#define isprint(x) sane_istest(x,GIT_PRINT)
+#define islower(x) (sane_istest(x,GIT_ALPHA) && sane_istest(x,0x20))
+#define isupper(x) (sane_istest(x,GIT_ALPHA) && !sane_istest(x,0x20))
#define tolower(x) sane_case((unsigned char)(x), 0x20)
#define toupper(x) sane_case((unsigned char)(x), 0)
@@ -245,6 +249,8 @@ struct perf_event_attr;
void event_attr_init(struct perf_event_attr *attr);
+uid_t parse_target_uid(const char *str, const char *tid, const char *pid);
+
#define _STR(x) #x
#define STR(x) _STR(x)
diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile
index e8a03aceceb..a93e06cfcc2 100644
--- a/tools/power/cpupower/Makefile
+++ b/tools/power/cpupower/Makefile
@@ -19,6 +19,16 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
+OUTPUT=./
+ifeq ("$(origin O)", "command line")
+ OUTPUT := $(O)/
+endif
+
+ifneq ($(OUTPUT),)
+# check that the output directory actually exists
+OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
+$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
+endif
# --- CONFIGURATION BEGIN ---
@@ -87,6 +97,7 @@ AR = $(CROSS)ar
STRIP = $(CROSS)strip
RANLIB = $(CROSS)ranlib
HOSTCC = gcc
+MKDIR = mkdir
# Now we set up the build system
@@ -95,7 +106,7 @@ HOSTCC = gcc
# set up PWD so that older versions of make will work with our build.
PWD = $(shell pwd)
-GMO_FILES = ${shell for HLANG in ${LANGUAGES}; do echo po/$$HLANG.gmo; done;}
+GMO_FILES = ${shell for HLANG in ${LANGUAGES}; do echo $(OUTPUT)po/$$HLANG.gmo; done;}
export CROSS CC AR STRIP RANLIB CFLAGS LDFLAGS LIB_OBJS
@@ -122,15 +133,18 @@ UTIL_OBJS = utils/helpers/amd.o utils/helpers/topology.o utils/helpers/msr.o \
utils/cpupower.o utils/cpufreq-info.o utils/cpufreq-set.o \
utils/cpupower-set.o utils/cpupower-info.o utils/cpuidle-info.o
+UTIL_SRC := $(UTIL_OBJS:.o=.c)
+
+UTIL_OBJS := $(addprefix $(OUTPUT),$(UTIL_OBJS))
+
UTIL_HEADERS = utils/helpers/helpers.h utils/idle_monitor/cpupower-monitor.h \
utils/helpers/bitmask.h \
utils/idle_monitor/idle_monitors.h utils/idle_monitor/idle_monitors.def
-UTIL_SRC := $(UTIL_OBJS:.o=.c)
-
LIB_HEADERS = lib/cpufreq.h lib/sysfs.h
LIB_SRC = lib/cpufreq.c lib/sysfs.c
LIB_OBJS = lib/cpufreq.o lib/sysfs.o
+LIB_OBJS := $(addprefix $(OUTPUT),$(LIB_OBJS))
CFLAGS += -pipe
@@ -168,83 +182,90 @@ endif
# the actual make rules
-all: libcpupower cpupower $(COMPILE_NLS) $(COMPILE_BENCH)
+all: libcpupower $(OUTPUT)cpupower $(COMPILE_NLS) $(COMPILE_BENCH)
-lib/%.o: $(LIB_SRC) $(LIB_HEADERS)
+$(OUTPUT)lib/%.o: $(LIB_SRC) $(LIB_HEADERS)
$(ECHO) " CC " $@
$(QUIET) $(CC) $(CFLAGS) -fPIC -o $@ -c lib/$*.c
-libcpupower.so.$(LIB_MAJ): $(LIB_OBJS)
+$(OUTPUT)libcpupower.so.$(LIB_MAJ): $(LIB_OBJS)
$(ECHO) " LD " $@
$(QUIET) $(CC) -shared $(CFLAGS) $(LDFLAGS) -o $@ \
-Wl,-soname,libcpupower.so.$(LIB_MIN) $(LIB_OBJS)
- @ln -sf $@ libcpupower.so
- @ln -sf $@ libcpupower.so.$(LIB_MIN)
+ @ln -sf $(@F) $(OUTPUT)libcpupower.so
+ @ln -sf $(@F) $(OUTPUT)libcpupower.so.$(LIB_MIN)
-libcpupower: libcpupower.so.$(LIB_MAJ)
+libcpupower: $(OUTPUT)libcpupower.so.$(LIB_MAJ)
# Let all .o files depend on its .c file and all headers
# Might be worth to put this into utils/Makefile at some point of time
$(UTIL_OBJS): $(UTIL_HEADERS)
-.c.o:
+$(OUTPUT)%.o: %.c
$(ECHO) " CC " $@
$(QUIET) $(CC) $(CFLAGS) -I./lib -I ./utils -o $@ -c $*.c
-cpupower: $(UTIL_OBJS) libcpupower.so.$(LIB_MAJ)
+$(OUTPUT)cpupower: $(UTIL_OBJS) $(OUTPUT)libcpupower.so.$(LIB_MAJ)
$(ECHO) " CC " $@
- $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) -lcpupower -lrt -lpci -L. -o $@ $(UTIL_OBJS)
+ $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lcpupower -lrt -lpci -L$(OUTPUT) -o $@
$(QUIET) $(STRIPCMD) $@
-po/$(PACKAGE).pot: $(UTIL_SRC)
+$(OUTPUT)po/$(PACKAGE).pot: $(UTIL_SRC)
$(ECHO) " GETTEXT " $@
$(QUIET) xgettext --default-domain=$(PACKAGE) --add-comments \
- --keyword=_ --keyword=N_ $(UTIL_SRC) && \
- test -f $(PACKAGE).po && \
- mv -f $(PACKAGE).po po/$(PACKAGE).pot
+ --keyword=_ --keyword=N_ $(UTIL_SRC) -p $(@D) -o $(@F)
-po/%.gmo: po/%.po
+$(OUTPUT)po/%.gmo: po/%.po
$(ECHO) " MSGFMT " $@
$(QUIET) msgfmt -o $@ po/$*.po
create-gmo: ${GMO_FILES}
-update-po: po/$(PACKAGE).pot
+update-po: $(OUTPUT)po/$(PACKAGE).pot
$(ECHO) " MSGMRG " $@
$(QUIET) @for HLANG in $(LANGUAGES); do \
echo -n "Updating $$HLANG "; \
- if msgmerge po/$$HLANG.po po/$(PACKAGE).pot -o \
- po/$$HLANG.new.po; then \
- mv -f po/$$HLANG.new.po po/$$HLANG.po; \
+ if msgmerge po/$$HLANG.po $< -o \
+ $(OUTPUT)po/$$HLANG.new.po; then \
+ mv -f $(OUTPUT)po/$$HLANG.new.po $(OUTPUT)po/$$HLANG.po; \
else \
echo "msgmerge for $$HLANG failed!"; \
- rm -f po/$$HLANG.new.po; \
+ rm -f $(OUTPUT)po/$$HLANG.new.po; \
fi; \
done;
-compile-bench: libcpupower.so.$(LIB_MAJ)
- @V=$(V) confdir=$(confdir) $(MAKE) -C bench
+compile-bench: $(OUTPUT)libcpupower.so.$(LIB_MAJ)
+ @V=$(V) confdir=$(confdir) $(MAKE) -C bench O=$(OUTPUT)
+
+# we compile into subdirectories. if the target directory is not the
+# source directory, they might not exists. So we depend the various
+# files onto their directories.
+DIRECTORY_DEPS = $(LIB_OBJS) $(UTIL_OBJS) $(GMO_FILES)
+$(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS)))
+
+# In the second step, we make a rule to actually create these directories
+$(sort $(dir $(DIRECTORY_DEPS))):
+ $(ECHO) " MKDIR " $@
+ $(QUIET) $(MKDIR) -p $@ 2>/dev/null
clean:
- -find . \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' \) -type f -print \
+ -find $(OUTPUT) \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' \) -type f -print \
| xargs rm -f
- -rm -f $(UTIL_BINS)
- -rm -f $(IDLE_OBJS)
- -rm -f cpupower
- -rm -f libcpupower.so*
- -rm -rf po/*.gmo po/*.pot
- $(MAKE) -C bench clean
+ -rm -f $(OUTPUT)cpupower
+ -rm -f $(OUTPUT)libcpupower.so*
+ -rm -rf $(OUTPUT)po/*.{gmo,pot}
+ $(MAKE) -C bench O=$(OUTPUT) clean
install-lib:
$(INSTALL) -d $(DESTDIR)${libdir}
- $(CP) libcpupower.so* $(DESTDIR)${libdir}/
+ $(CP) $(OUTPUT)libcpupower.so* $(DESTDIR)${libdir}/
$(INSTALL) -d $(DESTDIR)${includedir}
$(INSTALL_DATA) lib/cpufreq.h $(DESTDIR)${includedir}/cpufreq.h
install-tools:
$(INSTALL) -d $(DESTDIR)${bindir}
- $(INSTALL_PROGRAM) cpupower $(DESTDIR)${bindir}
+ $(INSTALL_PROGRAM) $(OUTPUT)cpupower $(DESTDIR)${bindir}
install-man:
$(INSTALL_DATA) -D man/cpupower.1 $(DESTDIR)${mandir}/man1/cpupower.1
@@ -257,13 +278,13 @@ install-man:
install-gmo:
$(INSTALL) -d $(DESTDIR)${localedir}
for HLANG in $(LANGUAGES); do \
- echo '$(INSTALL_DATA) -D po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo'; \
- $(INSTALL_DATA) -D po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo; \
+ echo '$(INSTALL_DATA) -D $(OUTPUT)po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo'; \
+ $(INSTALL_DATA) -D $(OUTPUT)po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo; \
done;
install-bench:
@#DESTDIR must be set from outside to survive
- @sbindir=$(sbindir) bindir=$(bindir) docdir=$(docdir) confdir=$(confdir) $(MAKE) -C bench install
+ @sbindir=$(sbindir) bindir=$(bindir) docdir=$(docdir) confdir=$(confdir) $(MAKE) -C bench O=$(OUTPUT) install
install: all install-lib install-tools install-man $(INSTALL_NLS) $(INSTALL_BENCH)
diff --git a/tools/power/cpupower/bench/Makefile b/tools/power/cpupower/bench/Makefile
index 2b67606fc3e..7ec7021a29c 100644
--- a/tools/power/cpupower/bench/Makefile
+++ b/tools/power/cpupower/bench/Makefile
@@ -1,29 +1,36 @@
-LIBS = -L../ -lm -lcpupower
+OUTPUT := ./
+ifeq ("$(origin O)", "command line")
+ifneq ($(O),)
+ OUTPUT := $(O)/
+endif
+endif
-OBJS = main.o parse.o system.o benchmark.o
+LIBS = -L../ -L$(OUTPUT) -lm -lcpupower
+
+OBJS = $(OUTPUT)main.o $(OUTPUT)parse.o $(OUTPUT)system.o $(OUTPUT)benchmark.o
CFLAGS += -D_GNU_SOURCE -I../lib -DDEFAULT_CONFIG_FILE=\"$(confdir)/cpufreq-bench.conf\"
-%.o : %.c
+$(OUTPUT)%.o : %.c
$(ECHO) " CC " $@
$(QUIET) $(CC) -c $(CFLAGS) $< -o $@
-cpufreq-bench: $(OBJS)
+$(OUTPUT)cpufreq-bench: $(OBJS)
$(ECHO) " CC " $@
$(QUIET) $(CC) -o $@ $(CFLAGS) $(OBJS) $(LIBS)
-all: cpufreq-bench
+all: $(OUTPUT)cpufreq-bench
install:
mkdir -p $(DESTDIR)/$(sbindir)
mkdir -p $(DESTDIR)/$(bindir)
mkdir -p $(DESTDIR)/$(docdir)
mkdir -p $(DESTDIR)/$(confdir)
- install -m 755 cpufreq-bench $(DESTDIR)/$(sbindir)/cpufreq-bench
+ install -m 755 $(OUTPUT)cpufreq-bench $(DESTDIR)/$(sbindir)/cpufreq-bench
install -m 755 cpufreq-bench_plot.sh $(DESTDIR)/$(bindir)/cpufreq-bench_plot.sh
install -m 644 README-BENCH $(DESTDIR)/$(docdir)/README-BENCH
install -m 755 cpufreq-bench_script.sh $(DESTDIR)/$(docdir)/cpufreq-bench_script.sh
install -m 644 example.cfg $(DESTDIR)/$(confdir)/cpufreq-bench.conf
clean:
- rm -f *.o
- rm -f cpufreq-bench
+ rm -f $(OUTPUT)*.o
+ rm -f $(OUTPUT)cpufreq-bench
diff --git a/tools/power/cpupower/debug/i386/Makefile b/tools/power/cpupower/debug/i386/Makefile
index d08cc1ead9b..3ba158f0e28 100644
--- a/tools/power/cpupower/debug/i386/Makefile
+++ b/tools/power/cpupower/debug/i386/Makefile
@@ -1,20 +1,38 @@
+OUTPUT=./
+ifeq ("$(origin O)", "command line")
+ OUTPUT := $(O)/
+endif
+
+DESTDIR =
+bindir = /usr/bin
+
+INSTALL = /usr/bin/install
+
+
default: all
-centrino-decode: centrino-decode.c
- $(CC) $(CFLAGS) -o centrino-decode centrino-decode.c
+$(OUTPUT)centrino-decode: centrino-decode.c
+ $(CC) $(CFLAGS) -o $@ centrino-decode.c
-dump_psb: dump_psb.c
- $(CC) $(CFLAGS) -o dump_psb dump_psb.c
+$(OUTPUT)dump_psb: dump_psb.c
+ $(CC) $(CFLAGS) -o $@ dump_psb.c
-intel_gsic: intel_gsic.c
- $(CC) $(CFLAGS) -o intel_gsic -llrmi intel_gsic.c
+$(OUTPUT)intel_gsic: intel_gsic.c
+ $(CC) $(CFLAGS) -o $@ -llrmi intel_gsic.c
-powernow-k8-decode: powernow-k8-decode.c
- $(CC) $(CFLAGS) -o powernow-k8-decode powernow-k8-decode.c
+$(OUTPUT)powernow-k8-decode: powernow-k8-decode.c
+ $(CC) $(CFLAGS) -o $@ powernow-k8-decode.c
-all: centrino-decode dump_psb intel_gsic powernow-k8-decode
+all: $(OUTPUT)centrino-decode $(OUTPUT)dump_psb $(OUTPUT)intel_gsic $(OUTPUT)powernow-k8-decode
clean:
- rm -rf centrino-decode dump_psb intel_gsic powernow-k8-decode
+ rm -rf $(OUTPUT){centrino-decode,dump_psb,intel_gsic,powernow-k8-decode}
+
+install:
+ $(INSTALL) -d $(DESTDIR)${bindir}
+ $(INSTALL) $(OUTPUT)centrino-decode $(DESTDIR)${bindir}
+ $(INSTALL) $(OUTPUT)powernow-k8-decode $(DESTDIR)${bindir}
+ $(INSTALL) $(OUTPUT)dump_psb $(DESTDIR)${bindir}
+ $(INSTALL) $(OUTPUT)intel_gsic $(DESTDIR)${bindir}
-.PHONY: all default clean
+.PHONY: all default clean install
diff --git a/tools/power/cpupower/debug/x86_64/Makefile b/tools/power/cpupower/debug/x86_64/Makefile
index 3326217dd31..1c521452671 100644
--- a/tools/power/cpupower/debug/x86_64/Makefile
+++ b/tools/power/cpupower/debug/x86_64/Makefile
@@ -1,14 +1,30 @@
+OUTPUT=./
+ifeq ("$(origin O)", "command line")
+ OUTPUT := $(O)/
+endif
+
+DESTDIR =
+bindir = /usr/bin
+
+INSTALL = /usr/bin/install
+
+
default: all
-centrino-decode: ../i386/centrino-decode.c
+$(OUTPUT)centrino-decode: ../i386/centrino-decode.c
$(CC) $(CFLAGS) -o $@ $<
-powernow-k8-decode: ../i386/powernow-k8-decode.c
+$(OUTPUT)powernow-k8-decode: ../i386/powernow-k8-decode.c
$(CC) $(CFLAGS) -o $@ $<
-all: centrino-decode powernow-k8-decode
+all: $(OUTPUT)centrino-decode $(OUTPUT)powernow-k8-decode
clean:
- rm -rf centrino-decode powernow-k8-decode
+ rm -rf $(OUTPUT)centrino-decode $(OUTPUT)powernow-k8-decode
+
+install:
+ $(INSTALL) -d $(DESTDIR)${bindir}
+ $(INSTALL) $(OUTPUT)centrino-decode $(DESTDIR)${bindir}
+ $(INSTALL) $(OUTPUT)powernow-k8-decode $(DESTDIR)${bindir}
-.PHONY: all default clean
+.PHONY: all default clean install
diff --git a/tools/power/cpupower/man/cpupower-frequency-info.1 b/tools/power/cpupower/man/cpupower-frequency-info.1
index bb60a8d1e45..4a1918ea8f9 100644
--- a/tools/power/cpupower/man/cpupower-frequency-info.1
+++ b/tools/power/cpupower/man/cpupower-frequency-info.1
@@ -1,4 +1,4 @@
-.TH "cpupower-frequency-info" "1" "0.1" "Mattia Dongili" ""
+.TH "CPUPOWER\-FREQUENCY\-INFO" "1" "0.1" "" "cpupower Manual"
.SH "NAME"
.LP
cpupower frequency\-info \- Utility to retrieve cpufreq kernel information
@@ -50,8 +50,6 @@ Prints out information like provided by the /proc/cpufreq interface in 2.4. and
\fB\-m\fR \fB\-\-human\fR
human\-readable output for the \-f, \-w, \-s and \-y parameters.
.TP
-\fB\-h\fR \fB\-\-help\fR
-Prints out the help screen.
.SH "REMARKS"
.LP
By default only values of core zero are displayed. How to display settings of
diff --git a/tools/power/cpupower/man/cpupower-frequency-set.1 b/tools/power/cpupower/man/cpupower-frequency-set.1
index 685f469093a..3eacc8d03d1 100644
--- a/tools/power/cpupower/man/cpupower-frequency-set.1
+++ b/tools/power/cpupower/man/cpupower-frequency-set.1
@@ -1,4 +1,4 @@
-.TH "cpupower-freqency-set" "1" "0.1" "Mattia Dongili" ""
+.TH "CPUPOWER\-FREQUENCY\-SET" "1" "0.1" "" "cpupower Manual"
.SH "NAME"
.LP
cpupower frequency\-set \- A small tool which allows to modify cpufreq settings.
@@ -26,8 +26,6 @@ specific frequency to be set. Requires userspace governor to be available and lo
\fB\-r\fR \fB\-\-related\fR
modify all hardware-related CPUs at the same time
.TP
-\fB\-h\fR \fB\-\-help\fR
-Prints out the help screen.
.SH "REMARKS"
.LP
By default values are applied on all cores. How to modify single core
diff --git a/tools/power/cpupower/man/cpupower-idle-info.1 b/tools/power/cpupower/man/cpupower-idle-info.1
new file mode 100644
index 00000000000..4178effd9e9
--- /dev/null
+++ b/tools/power/cpupower/man/cpupower-idle-info.1
@@ -0,0 +1,90 @@
+.TH "CPUPOWER-IDLE-INFO" "1" "0.1" "" "cpupower Manual"
+.SH "NAME"
+.LP
+cpupower idle\-info \- Utility to retrieve cpu idle kernel information
+.SH "SYNTAX"
+.LP
+cpupower [ \-c cpulist ] idle\-info [\fIoptions\fP]
+.SH "DESCRIPTION"
+.LP
+A tool which prints out per cpu idle information helpful to developers and interested users.
+.SH "OPTIONS"
+.LP
+.TP
+\fB\-f\fR \fB\-\-silent\fR
+Only print a summary of all available C-states in the system.
+.TP
+\fB\-e\fR \fB\-\-proc\fR
+deprecated.
+Prints out idle information in old /proc/acpi/processor/*/power format. This
+interface has been removed from the kernel for quite some time, do not let
+further code depend on this option, best do not use it.
+
+.SH IDLE\-INFO DESCRIPTIONS
+CPU sleep state statistics and descriptions are retrieved from sysfs files,
+exported by the cpuidle kernel subsystem. The kernel only updates these
+statistics when it enters or leaves an idle state, therefore on a very idle or
+a very busy system, these statistics may not be accurate. They still provide a
+good overview about the usage and availability of processor sleep states on
+the platform.
+
+Be aware that the sleep states as exported by the hardware or BIOS and used by
+the Linux kernel may not exactly reflect the capabilities of the
+processor. This often is the case on the X86 architecture when the acpi_idle
+driver is used. It is also possible that the hardware overrules the kernel
+requests, due to internal activity monitors or other reasons.
+On recent X86 platforms it is often possible to read out hardware registers
+which monitor the duration of sleep states the processor resided in. The
+cpupower monitor tool (cpupower\-monitor(1)) can be used to show real sleep
+state residencies. Please refer to the architecture specific description
+section below.
+
+.SH IDLE\-INFO ARCHITECTURE SPECIFIC DESCRIPTIONS
+.SS "X86"
+POLL idle state
+
+If cpuidle is active, X86 platforms have one special idle state.
+The POLL idle state is not a real idle state, it does not save any
+power. Instead, a busy\-loop is executed doing nothing for a short period of
+time. This state is used if the kernel knows that work has to be processed
+very soon and entering any real hardware idle state may result in a slight
+performance penalty.
+
+There exist two different cpuidle drivers on the X86 architecture platform:
+
+"acpi_idle" cpuidle driver
+
+The acpi_idle cpuidle driver retrieves available sleep states (C\-states) from
+the ACPI BIOS tables (from the _CST ACPI function on recent platforms or from
+the FADT BIOS table on older ones).
+The C1 state is not retrieved from ACPI tables. If the C1 state is entered,
+the kernel will call the hlt instruction (or mwait on Intel).
+
+"intel_idle" cpuidle driver
+
+In kernel 2.6.36 the intel_idle driver was introduced.
+It only serves recent Intel CPUs (Nehalem, Westmere, Sandybridge, Atoms or
+newer). On older Intel CPUs the acpi_idle driver is still used (if the BIOS
+provides C\-state ACPI tables).
+The intel_idle driver knows the sleep state capabilities of the processor and
+ignores ACPI BIOS exported processor sleep states tables.
+
+.SH "REMARKS"
+.LP
+By default only values of core zero are displayed. How to display settings of
+other cores is described in the cpupower(1) manpage in the \-\-cpu option
+section.
+.SH REFERENCES
+http://www.acpi.info/spec.htm
+.SH "FILES"
+.nf
+\fI/sys/devices/system/cpu/cpu*/cpuidle/state*\fP
+\fI/sys/devices/system/cpu/cpuidle/*\fP
+.fi
+.SH "AUTHORS"
+.nf
+Thomas Renninger <trenn@suse.de>
+.fi
+.SH "SEE ALSO"
+.LP
+cpupower(1), cpupower\-monitor(1), cpupower\-info(1), cpupower\-set(1)
diff --git a/tools/power/cpupower/man/cpupower-monitor.1 b/tools/power/cpupower/man/cpupower-monitor.1
index d5cfa265c3d..1141c207371 100644
--- a/tools/power/cpupower/man/cpupower-monitor.1
+++ b/tools/power/cpupower/man/cpupower-monitor.1
@@ -107,7 +107,7 @@ Deepest package sleep states may in reality show up as machine/platform wide
sleep states and can only be entered if all cores are idle. Look up Intel
manuals (some are provided in the References section) for further details.
-.SS "Ontario" "Liano"
+.SS "Fam_12h" "Fam_14h"
AMD laptop and desktop processor (family 12h and 14h) sleep state counters.
The registers are accessed via PCI and therefore can still be read out while
cores have been offlined.
diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c
index b028267c137..8145af5f93a 100644
--- a/tools/power/cpupower/utils/cpuidle-info.c
+++ b/tools/power/cpupower/utils/cpuidle-info.c
@@ -35,17 +35,9 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose)
printf(_("CPU %u: Can't read idle state info\n"), cpu);
return;
}
- tmp = sysfs_get_idlestate_name(cpu, idlestates - 1);
- if (!tmp) {
- printf(_("Could not determine max idle state %u\n"),
- idlestates - 1);
- return;
- }
-
printf(_("Number of idle states: %d\n"), idlestates);
-
printf(_("Available idle states:"));
- for (idlestate = 1; idlestate < idlestates; idlestate++) {
+ for (idlestate = 0; idlestate < idlestates; idlestate++) {
tmp = sysfs_get_idlestate_name(cpu, idlestate);
if (!tmp)
continue;
@@ -57,7 +49,7 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose)
if (!verbose)
return;
- for (idlestate = 1; idlestate < idlestates; idlestate++) {
+ for (idlestate = 0; idlestate < idlestates; idlestate++) {
tmp = sysfs_get_idlestate_name(cpu, idlestate);
if (!tmp)
continue;
diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c
index 87d5605bdda..6437ef39aee 100644
--- a/tools/power/cpupower/utils/helpers/amd.c
+++ b/tools/power/cpupower/utils/helpers/amd.c
@@ -112,14 +112,12 @@ int decode_pstates(unsigned int cpu, unsigned int cpu_family,
int amd_pci_get_num_boost_states(int *active, int *states)
{
struct pci_access *pci_acc;
- int vendor_id = 0x1022;
- int boost_dev_ids[4] = {0x1204, 0x1604, 0x1704, 0};
struct pci_dev *device;
uint8_t val = 0;
*active = *states = 0;
- device = pci_acc_init(&pci_acc, vendor_id, boost_dev_ids);
+ device = pci_slot_func_init(&pci_acc, 0x18, 4);
if (device == NULL)
return -ENODEV;
diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h
index 2747e738efb..2eb584cf2f5 100644
--- a/tools/power/cpupower/utils/helpers/helpers.h
+++ b/tools/power/cpupower/utils/helpers/helpers.h
@@ -66,8 +66,8 @@ enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL,
#define CPUPOWER_CAP_AMD_CBP 0x00000004
#define CPUPOWER_CAP_PERF_BIAS 0x00000008
#define CPUPOWER_CAP_HAS_TURBO_RATIO 0x00000010
-#define CPUPOWER_CAP_IS_SNB 0x00000011
-#define CPUPOWER_CAP_INTEL_IDA 0x00000012
+#define CPUPOWER_CAP_IS_SNB 0x00000020
+#define CPUPOWER_CAP_INTEL_IDA 0x00000040
#define MAX_HW_PSTATES 10
@@ -132,8 +132,11 @@ extern unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu);
/* PCI stuff ****************************/
extern int amd_pci_get_num_boost_states(int *active, int *states);
-extern struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id,
- int *dev_ids);
+extern struct pci_dev *pci_acc_init(struct pci_access **pacc, int domain,
+ int bus, int slot, int func, int vendor,
+ int dev);
+extern struct pci_dev *pci_slot_func_init(struct pci_access **pacc,
+ int slot, int func);
/* PCI stuff ****************************/
diff --git a/tools/power/cpupower/utils/helpers/pci.c b/tools/power/cpupower/utils/helpers/pci.c
index cd2eb6fe41c..9690798e644 100644
--- a/tools/power/cpupower/utils/helpers/pci.c
+++ b/tools/power/cpupower/utils/helpers/pci.c
@@ -10,19 +10,24 @@
* **pacc : if a valid pci_dev is returned
* *pacc must be passed to pci_acc_cleanup to free it
*
- * vendor_id : the pci vendor id matching the pci device to access
- * dev_ids : device ids matching the pci device to access
+ * domain: domain
+ * bus: bus
+ * slot: slot
+ * func: func
+ * vendor: vendor
+ * device: device
+ * Pass -1 for one of the six above to match any
*
* Returns :
* struct pci_dev which can be used with pci_{read,write}_* functions
* to access the PCI config space of matching pci devices
*/
-struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id,
- int *dev_ids)
+struct pci_dev *pci_acc_init(struct pci_access **pacc, int domain, int bus,
+ int slot, int func, int vendor, int dev)
{
- struct pci_filter filter_nb_link = { -1, -1, -1, -1, vendor_id, 0};
+ struct pci_filter filter_nb_link = { domain, bus, slot, func,
+ vendor, dev };
struct pci_dev *device;
- unsigned int i;
*pacc = pci_alloc();
if (*pacc == NULL)
@@ -31,14 +36,20 @@ struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id,
pci_init(*pacc);
pci_scan_bus(*pacc);
- for (i = 0; dev_ids[i] != 0; i++) {
- filter_nb_link.device = dev_ids[i];
- for (device = (*pacc)->devices; device; device = device->next) {
- if (pci_filter_match(&filter_nb_link, device))
- return device;
- }
+ for (device = (*pacc)->devices; device; device = device->next) {
+ if (pci_filter_match(&filter_nb_link, device))
+ return device;
}
pci_cleanup(*pacc);
return NULL;
}
+
+/* Typically one wants to get a specific slot(device)/func of the root domain
+ and bus */
+struct pci_dev *pci_slot_func_init(struct pci_access **pacc, int slot,
+ int func)
+{
+ return pci_acc_init(pacc, 0, 0, slot, func, -1, -1);
+}
+
#endif /* defined(__i386__) || defined(__x86_64__) */
diff --git a/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c b/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c
index 202e555988b..2116df9ad83 100644
--- a/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c
+++ b/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c
@@ -20,8 +20,6 @@
#include "idle_monitor/cpupower-monitor.h"
#include "helpers/helpers.h"
-/******** PCI parts could go into own file and get shared ***************/
-
#define PCI_NON_PC0_OFFSET 0xb0
#define PCI_PC1_OFFSET 0xb4
#define PCI_PC6_OFFSET 0xb8
@@ -82,10 +80,7 @@ static cstate_t amd_fam14h_cstates[AMD_FAM14H_STATE_NUM] = {
};
static struct pci_access *pci_acc;
-static int pci_vendor_id = 0x1022;
-static int pci_dev_ids[2] = {0x1716, 0};
static struct pci_dev *amd_fam14h_pci_dev;
-
static int nbp1_entered;
struct timespec start_time;
@@ -286,13 +281,13 @@ struct cpuidle_monitor *amd_fam14h_register(void)
if (cpupower_cpu_info.vendor != X86_VENDOR_AMD)
return NULL;
- if (cpupower_cpu_info.family == 0x14) {
- if (cpu_count <= 0 || cpu_count > 2) {
- fprintf(stderr, "AMD fam14h: Invalid cpu count: %d\n",
- cpu_count);
- return NULL;
- }
- } else
+ if (cpupower_cpu_info.family == 0x14)
+ strncpy(amd_fam14h_monitor.name, "Fam_14h",
+ MONITOR_NAME_LEN - 1);
+ else if (cpupower_cpu_info.family == 0x12)
+ strncpy(amd_fam14h_monitor.name, "Fam_12h",
+ MONITOR_NAME_LEN - 1);
+ else
return NULL;
/* We do not alloc for nbp1 machine wide counter */
@@ -303,7 +298,9 @@ struct cpuidle_monitor *amd_fam14h_register(void)
sizeof(unsigned long long));
}
- amd_fam14h_pci_dev = pci_acc_init(&pci_acc, pci_vendor_id, pci_dev_ids);
+ /* We need PCI device: Slot 18, Func 6, compare with BKDG
+ for fam 12h/14h */
+ amd_fam14h_pci_dev = pci_slot_func_init(&pci_acc, 0x18, 6);
if (amd_fam14h_pci_dev == NULL || pci_acc == NULL)
return NULL;
@@ -325,7 +322,7 @@ static void amd_fam14h_unregister(void)
}
struct cpuidle_monitor amd_fam14h_monitor = {
- .name = "Ontario",
+ .name = "",
.hw_states = amd_fam14h_cstates,
.hw_states_num = AMD_FAM14H_STATE_NUM,
.start = amd_fam14h_start,
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
index 9507c4b251a..95d6a6f7c33 100755
--- a/tools/testing/ktest/ktest.pl
+++ b/tools/testing/ktest/ktest.pl
@@ -46,6 +46,7 @@ my %default = (
"DIE_ON_FAILURE" => 1,
"SSH_EXEC" => "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND",
"SCP_TO_TARGET" => "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE",
+ "SCP_TO_TARGET_INSTALL" => "\${SCP_TO_TARGET}",
"REBOOT" => "ssh \$SSH_USER\@\$MACHINE reboot",
"STOP_AFTER_SUCCESS" => 10,
"STOP_AFTER_FAILURE" => 60,
@@ -86,11 +87,13 @@ my $reboot_on_error;
my $switch_to_good;
my $switch_to_test;
my $poweroff_on_error;
+my $reboot_on_success;
my $die_on_failure;
my $powercycle_after_reboot;
my $poweroff_after_halt;
my $ssh_exec;
my $scp_to_target;
+my $scp_to_target_install;
my $power_off;
my $grub_menu;
my $grub_number;
@@ -211,6 +214,7 @@ my %option_map = (
"SWITCH_TO_GOOD" => \$switch_to_good,
"SWITCH_TO_TEST" => \$switch_to_test,
"POWEROFF_ON_ERROR" => \$poweroff_on_error,
+ "REBOOT_ON_SUCCESS" => \$reboot_on_success,
"DIE_ON_FAILURE" => \$die_on_failure,
"POWER_OFF" => \$power_off,
"POWERCYCLE_AFTER_REBOOT" => \$powercycle_after_reboot,
@@ -243,6 +247,7 @@ my %option_map = (
"BUILD_TARGET" => \$build_target,
"SSH_EXEC" => \$ssh_exec,
"SCP_TO_TARGET" => \$scp_to_target,
+ "SCP_TO_TARGET_INSTALL" => \$scp_to_target_install,
"CHECKOUT" => \$checkout,
"TARGET_IMAGE" => \$target_image,
"LOCALVERSION" => \$localversion,
@@ -1113,7 +1118,6 @@ sub reboot_to_good {
if (defined($switch_to_good)) {
run_command $switch_to_good;
- return;
}
reboot $time;
@@ -1349,8 +1353,7 @@ sub run_ssh {
}
sub run_scp {
- my ($src, $dst) = @_;
- my $cp_scp = $scp_to_target;
+ my ($src, $dst, $cp_scp) = @_;
$cp_scp =~ s/\$SRC_FILE/$src/g;
$cp_scp =~ s/\$DST_FILE/$dst/g;
@@ -1358,6 +1361,22 @@ sub run_scp {
return run_command "$cp_scp";
}
+sub run_scp_install {
+ my ($src, $dst) = @_;
+
+ my $cp_scp = $scp_to_target_install;
+
+ return run_scp($src, $dst, $cp_scp);
+}
+
+sub run_scp_mod {
+ my ($src, $dst) = @_;
+
+ my $cp_scp = $scp_to_target;
+
+ return run_scp($src, $dst, $cp_scp);
+}
+
sub get_grub_index {
if ($reboot_type ne "grub") {
@@ -1460,6 +1479,7 @@ sub get_sha1 {
sub monitor {
my $booted = 0;
my $bug = 0;
+ my $bug_ignored = 0;
my $skip_call_trace = 0;
my $loops;
@@ -1531,9 +1551,13 @@ sub monitor {
}
if ($full_line =~ /call trace:/i) {
- if (!$ignore_errors && !$bug && !$skip_call_trace) {
- $bug = 1;
- $failure_start = time;
+ if (!$bug && !$skip_call_trace) {
+ if ($ignore_errors) {
+ $bug_ignored = 1;
+ } else {
+ $bug = 1;
+ $failure_start = time;
+ }
}
}
@@ -1595,6 +1619,10 @@ sub monitor {
fail "failed - never got a boot prompt." and return 0;
}
+ if ($bug_ignored) {
+ doprint "WARNING: Call Trace detected but ignored due to IGNORE_ERRORS=1\n";
+ }
+
return 1;
}
@@ -1621,7 +1649,7 @@ sub install {
my $cp_target = eval_kernel_version $target_image;
- run_scp "$outputdir/$build_target", "$cp_target" or
+ run_scp_install "$outputdir/$build_target", "$cp_target" or
dodie "failed to copy image";
my $install_mods = 0;
@@ -1643,7 +1671,7 @@ sub install {
return;
}
- run_command "$make INSTALL_MOD_PATH=$tmpdir modules_install" or
+ run_command "$make INSTALL_MOD_STRIP=1 INSTALL_MOD_PATH=$tmpdir modules_install" or
dodie "Failed to install modules";
my $modlib = "/lib/modules/$version";
@@ -1656,7 +1684,7 @@ sub install {
run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or
dodie "making tarball";
- run_scp "$tmpdir/$modtar", "/tmp" or
+ run_scp_mod "$tmpdir/$modtar", "/tmp" or
dodie "failed to copy modules";
unlink "$tmpdir/$modtar";
@@ -2601,7 +2629,7 @@ sub config_bisect {
# read directly what we want to check
my %config_check;
open (IN, $output_config)
- or dodie "faied to open $output_config";
+ or dodie "failed to open $output_config";
while (<IN>) {
if (/^((CONFIG\S*)=.*)/) {
@@ -3526,8 +3554,10 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) {
die "failed to checkout $checkout";
}
- $no_reboot = 0;
-
+ # A test may opt to not reboot the box
+ if ($reboot_on_success) {
+ $no_reboot = 0;
+ }
if ($test_type eq "bisect") {
bisect $i;
@@ -3572,8 +3602,12 @@ if ($opt{"POWEROFF_ON_SUCCESS"}) {
halt;
} elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) {
reboot_to_good;
+} elsif (defined($switch_to_good)) {
+ # still need to get to the good kernel
+ run_command $switch_to_good;
}
+
doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n";
exit 0;
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf
index 5ea04c6a71b..b682456afda 100644
--- a/tools/testing/ktest/sample.conf
+++ b/tools/testing/ktest/sample.conf
@@ -710,10 +710,18 @@
# The variables SSH_USER, MACHINE and SSH_COMMAND are defined
#SSH_EXEC = ssh $SSH_USER@$MACHINE $SSH_COMMAND";
-# The way to copy a file to the target
+# The way to copy a file to the target (install and modules)
# (default scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE)
-# The variables SSH_USER, MACHINE, SRC_FILE and DST_FILE are defined.
-#SCP_TO_TARGET = scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE
+# The variables SSH_USER, MACHINE are defined by the config
+# SRC_FILE and DST_FILE are ktest internal variables and
+# should only have '$' and not the '${}' notation.
+# (default scp $SRC_FILE ${SSH_USER}@${MACHINE}:$DST_FILE)
+#SCP_TO_TARGET = echo skip scp for $SRC_FILE $DST_FILE
+
+# If install needs to be different than modules, then this
+# option will override the SCP_TO_TARGET for installation.
+# (default ${SCP_TO_TARGET} )
+#SCP_TO_TARGET_INSTALL = scp $SRC_FILE tftp@tftpserver:$DST_FILE
# The nice way to reboot the target
# (default ssh $SSH_USER@$MACHINE reboot)
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 4ec84018cc1..28bc57ee757 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -1,10 +1,15 @@
-TARGETS = breakpoints
+TARGETS = breakpoints vm
all:
for TARGET in $(TARGETS); do \
make -C $$TARGET; \
done;
+run_tests: all
+ for TARGET in $(TARGETS); do \
+ make -C $$TARGET run_tests; \
+ done;
+
clean:
for TARGET in $(TARGETS); do \
make -C $$TARGET clean; \
diff --git a/tools/testing/selftests/breakpoints/Makefile b/tools/testing/selftests/breakpoints/Makefile
index f362722cdce..931278035f5 100644
--- a/tools/testing/selftests/breakpoints/Makefile
+++ b/tools/testing/selftests/breakpoints/Makefile
@@ -11,10 +11,13 @@ endif
all:
ifeq ($(ARCH),x86)
- gcc breakpoint_test.c -o run_test
+ gcc breakpoint_test.c -o breakpoint_test
else
echo "Not an x86 target, can't build breakpoints selftests"
endif
+run_tests:
+ ./breakpoint_test
+
clean:
- rm -fr run_test
+ rm -fr breakpoint_test
diff --git a/tools/testing/selftests/run_tests b/tools/testing/selftests/run_tests
deleted file mode 100644
index 320718a4e6b..00000000000
--- a/tools/testing/selftests/run_tests
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-TARGETS=breakpoints
-
-for TARGET in $TARGETS
-do
- $TARGET/run_test
-done
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile
new file mode 100644
index 00000000000..b336b24aa6c
--- /dev/null
+++ b/tools/testing/selftests/vm/Makefile
@@ -0,0 +1,14 @@
+# Makefile for vm selftests
+
+CC = $(CROSS_COMPILE)gcc
+CFLAGS = -Wall -Wextra
+
+all: hugepage-mmap hugepage-shm map_hugetlb
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+run_tests: all
+ /bin/sh ./run_vmtests
+
+clean:
+ $(RM) hugepage-mmap hugepage-shm map_hugetlb
diff --git a/tools/testing/selftests/vm/hugepage-mmap.c b/tools/testing/selftests/vm/hugepage-mmap.c
new file mode 100644
index 00000000000..a10f310d236
--- /dev/null
+++ b/tools/testing/selftests/vm/hugepage-mmap.c
@@ -0,0 +1,92 @@
+/*
+ * hugepage-mmap:
+ *
+ * Example of using huge page memory in a user application using the mmap
+ * system call. Before running this application, make sure that the
+ * administrator has mounted the hugetlbfs filesystem (on some directory
+ * like /mnt) using the command mount -t hugetlbfs nodev /mnt. In this
+ * example, the app is requesting memory of size 256MB that is backed by
+ * huge pages.
+ *
+ * For the ia64 architecture, the Linux kernel reserves Region number 4 for
+ * huge pages. That means that if one requires a fixed address, a huge page
+ * aligned address starting with 0x800000... will be required. If a fixed
+ * address is not required, the kernel will select an address in the proper
+ * range.
+ * Other architectures, such as ppc64, i386 or x86_64 are not so constrained.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+#define FILE_NAME "huge/hugepagefile"
+#define LENGTH (256UL*1024*1024)
+#define PROTECTION (PROT_READ | PROT_WRITE)
+
+/* Only ia64 requires this */
+#ifdef __ia64__
+#define ADDR (void *)(0x8000000000000000UL)
+#define FLAGS (MAP_SHARED | MAP_FIXED)
+#else
+#define ADDR (void *)(0x0UL)
+#define FLAGS (MAP_SHARED)
+#endif
+
+static void check_bytes(char *addr)
+{
+ printf("First hex is %x\n", *((unsigned int *)addr));
+}
+
+static void write_bytes(char *addr)
+{
+ unsigned long i;
+
+ for (i = 0; i < LENGTH; i++)
+ *(addr + i) = (char)i;
+}
+
+static int read_bytes(char *addr)
+{
+ unsigned long i;
+
+ check_bytes(addr);
+ for (i = 0; i < LENGTH; i++)
+ if (*(addr + i) != (char)i) {
+ printf("Mismatch at %lu\n", i);
+ return 1;
+ }
+ return 0;
+}
+
+int main(void)
+{
+ void *addr;
+ int fd, ret;
+
+ fd = open(FILE_NAME, O_CREAT | O_RDWR, 0755);
+ if (fd < 0) {
+ perror("Open failed");
+ exit(1);
+ }
+
+ addr = mmap(ADDR, LENGTH, PROTECTION, FLAGS, fd, 0);
+ if (addr == MAP_FAILED) {
+ perror("mmap");
+ unlink(FILE_NAME);
+ exit(1);
+ }
+
+ printf("Returned address is %p\n", addr);
+ check_bytes(addr);
+ write_bytes(addr);
+ ret = read_bytes(addr);
+
+ munmap(addr, LENGTH);
+ close(fd);
+ unlink(FILE_NAME);
+
+ return ret;
+}
diff --git a/tools/testing/selftests/vm/hugepage-shm.c b/tools/testing/selftests/vm/hugepage-shm.c
new file mode 100644
index 00000000000..0d0ef4fc0c0
--- /dev/null
+++ b/tools/testing/selftests/vm/hugepage-shm.c
@@ -0,0 +1,100 @@
+/*
+ * hugepage-shm:
+ *
+ * Example of using huge page memory in a user application using Sys V shared
+ * memory system calls. In this example the app is requesting 256MB of
+ * memory that is backed by huge pages. The application uses the flag
+ * SHM_HUGETLB in the shmget system call to inform the kernel that it is
+ * requesting huge pages.
+ *
+ * For the ia64 architecture, the Linux kernel reserves Region number 4 for
+ * huge pages. That means that if one requires a fixed address, a huge page
+ * aligned address starting with 0x800000... will be required. If a fixed
+ * address is not required, the kernel will select an address in the proper
+ * range.
+ * Other architectures, such as ppc64, i386 or x86_64 are not so constrained.
+ *
+ * Note: The default shared memory limit is quite low on many kernels,
+ * you may need to increase it via:
+ *
+ * echo 268435456 > /proc/sys/kernel/shmmax
+ *
+ * This will increase the maximum size per shared memory segment to 256MB.
+ * The other limit that you will hit eventually is shmall which is the
+ * total amount of shared memory in pages. To set it to 16GB on a system
+ * with a 4kB pagesize do:
+ *
+ * echo 4194304 > /proc/sys/kernel/shmall
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/mman.h>
+
+#ifndef SHM_HUGETLB
+#define SHM_HUGETLB 04000
+#endif
+
+#define LENGTH (256UL*1024*1024)
+
+#define dprintf(x) printf(x)
+
+/* Only ia64 requires this */
+#ifdef __ia64__
+#define ADDR (void *)(0x8000000000000000UL)
+#define SHMAT_FLAGS (SHM_RND)
+#else
+#define ADDR (void *)(0x0UL)
+#define SHMAT_FLAGS (0)
+#endif
+
+int main(void)
+{
+ int shmid;
+ unsigned long i;
+ char *shmaddr;
+
+ shmid = shmget(2, LENGTH, SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W);
+ if (shmid < 0) {
+ perror("shmget");
+ exit(1);
+ }
+ printf("shmid: 0x%x\n", shmid);
+
+ shmaddr = shmat(shmid, ADDR, SHMAT_FLAGS);
+ if (shmaddr == (char *)-1) {
+ perror("Shared memory attach failure");
+ shmctl(shmid, IPC_RMID, NULL);
+ exit(2);
+ }
+ printf("shmaddr: %p\n", shmaddr);
+
+ dprintf("Starting the writes:\n");
+ for (i = 0; i < LENGTH; i++) {
+ shmaddr[i] = (char)(i);
+ if (!(i % (1024 * 1024)))
+ dprintf(".");
+ }
+ dprintf("\n");
+
+ dprintf("Starting the Check...");
+ for (i = 0; i < LENGTH; i++)
+ if (shmaddr[i] != (char)i) {
+ printf("\nIndex %lu mismatched\n", i);
+ exit(3);
+ }
+ dprintf("Done.\n");
+
+ if (shmdt((const void *)shmaddr) != 0) {
+ perror("Detach failure");
+ shmctl(shmid, IPC_RMID, NULL);
+ exit(4);
+ }
+
+ shmctl(shmid, IPC_RMID, NULL);
+
+ return 0;
+}
diff --git a/tools/testing/selftests/vm/map_hugetlb.c b/tools/testing/selftests/vm/map_hugetlb.c
new file mode 100644
index 00000000000..ac56639dd4a
--- /dev/null
+++ b/tools/testing/selftests/vm/map_hugetlb.c
@@ -0,0 +1,79 @@
+/*
+ * Example of using hugepage memory in a user application using the mmap
+ * system call with MAP_HUGETLB flag. Before running this program make
+ * sure the administrator has allocated enough default sized huge pages
+ * to cover the 256 MB allocation.
+ *
+ * For ia64 architecture, Linux kernel reserves Region number 4 for hugepages.
+ * That means the addresses starting with 0x800000... will need to be
+ * specified. Specifying a fixed address is not required on ppc64, i386
+ * or x86_64.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+#define LENGTH (256UL*1024*1024)
+#define PROTECTION (PROT_READ | PROT_WRITE)
+
+#ifndef MAP_HUGETLB
+#define MAP_HUGETLB 0x40000 /* arch specific */
+#endif
+
+/* Only ia64 requires this */
+#ifdef __ia64__
+#define ADDR (void *)(0x8000000000000000UL)
+#define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_FIXED)
+#else
+#define ADDR (void *)(0x0UL)
+#define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB)
+#endif
+
+static void check_bytes(char *addr)
+{
+ printf("First hex is %x\n", *((unsigned int *)addr));
+}
+
+static void write_bytes(char *addr)
+{
+ unsigned long i;
+
+ for (i = 0; i < LENGTH; i++)
+ *(addr + i) = (char)i;
+}
+
+static int read_bytes(char *addr)
+{
+ unsigned long i;
+
+ check_bytes(addr);
+ for (i = 0; i < LENGTH; i++)
+ if (*(addr + i) != (char)i) {
+ printf("Mismatch at %lu\n", i);
+ return 1;
+ }
+ return 0;
+}
+
+int main(void)
+{
+ void *addr;
+ int ret;
+
+ addr = mmap(ADDR, LENGTH, PROTECTION, FLAGS, 0, 0);
+ if (addr == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+
+ printf("Returned address is %p\n", addr);
+ check_bytes(addr);
+ write_bytes(addr);
+ ret = read_bytes(addr);
+
+ munmap(addr, LENGTH);
+
+ return ret;
+}
diff --git a/tools/testing/selftests/vm/run_vmtests b/tools/testing/selftests/vm/run_vmtests
new file mode 100644
index 00000000000..8b40bd5e5cc
--- /dev/null
+++ b/tools/testing/selftests/vm/run_vmtests
@@ -0,0 +1,77 @@
+#!/bin/bash
+#please run as root
+
+#we need 256M, below is the size in kB
+needmem=262144
+mnt=./huge
+
+#get pagesize and freepages from /proc/meminfo
+while read name size unit; do
+ if [ "$name" = "HugePages_Free:" ]; then
+ freepgs=$size
+ fi
+ if [ "$name" = "Hugepagesize:" ]; then
+ pgsize=$size
+ fi
+done < /proc/meminfo
+
+#set proper nr_hugepages
+if [ -n "$freepgs" ] && [ -n "$pgsize" ]; then
+ nr_hugepgs=`cat /proc/sys/vm/nr_hugepages`
+ needpgs=`expr $needmem / $pgsize`
+ if [ $freepgs -lt $needpgs ]; then
+ lackpgs=$(( $needpgs - $freepgs ))
+ echo $(( $lackpgs + $nr_hugepgs )) > /proc/sys/vm/nr_hugepages
+ if [ $? -ne 0 ]; then
+ echo "Please run this test as root"
+ exit 1
+ fi
+ fi
+else
+ echo "no hugetlbfs support in kernel?"
+ exit 1
+fi
+
+mkdir $mnt
+mount -t hugetlbfs none $mnt
+
+echo "--------------------"
+echo "runing hugepage-mmap"
+echo "--------------------"
+./hugepage-mmap
+if [ $? -ne 0 ]; then
+ echo "[FAIL]"
+else
+ echo "[PASS]"
+fi
+
+shmmax=`cat /proc/sys/kernel/shmmax`
+shmall=`cat /proc/sys/kernel/shmall`
+echo 268435456 > /proc/sys/kernel/shmmax
+echo 4194304 > /proc/sys/kernel/shmall
+echo "--------------------"
+echo "runing hugepage-shm"
+echo "--------------------"
+./hugepage-shm
+if [ $? -ne 0 ]; then
+ echo "[FAIL]"
+else
+ echo "[PASS]"
+fi
+echo $shmmax > /proc/sys/kernel/shmmax
+echo $shmall > /proc/sys/kernel/shmall
+
+echo "--------------------"
+echo "runing map_hugetlb"
+echo "--------------------"
+./map_hugetlb
+if [ $? -ne 0 ]; then
+ echo "[FAIL]"
+else
+ echo "[PASS]"
+fi
+
+#cleanup
+umount $mnt
+rm -rf $mnt
+echo $nr_hugepgs > /proc/sys/vm/nr_hugepages
diff --git a/tools/usb/Makefile b/tools/usb/Makefile
index 8b704af1434..396d6c44e9d 100644
--- a/tools/usb/Makefile
+++ b/tools/usb/Makefile
@@ -3,7 +3,7 @@
CC = $(CROSS_COMPILE)gcc
PTHREAD_LIBS = -lpthread
WARNINGS = -Wall -Wextra
-CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS)
+CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) -I../include
all: testusb ffs-test
%: %.c
diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c
index b9c79863169..4b107b5e623 100644
--- a/tools/usb/ffs-test.c
+++ b/tools/usb/ffs-test.c
@@ -2,7 +2,7 @@
* ffs-test.c.c -- user mode filesystem api for usb composite function
*
* Copyright (C) 2010 Samsung Electronics
- * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ * Author: Michal Nazarewicz <mina86@mina86.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -36,6 +36,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <tools/le_byteshift.h>
#include "../../include/linux/usb/functionfs.h"
@@ -47,34 +48,6 @@
#define le32_to_cpu(x) le32toh(x)
#define le16_to_cpu(x) le16toh(x)
-static inline __u16 get_unaligned_le16(const void *_ptr)
-{
- const __u8 *ptr = _ptr;
- return ptr[0] | (ptr[1] << 8);
-}
-
-static inline __u32 get_unaligned_le32(const void *_ptr)
-{
- const __u8 *ptr = _ptr;
- return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
-}
-
-static inline void put_unaligned_le16(__u16 val, void *_ptr)
-{
- __u8 *ptr = _ptr;
- *ptr++ = val;
- *ptr++ = val >> 8;
-}
-
-static inline void put_unaligned_le32(__u32 val, void *_ptr)
-{
- __u8 *ptr = _ptr;
- *ptr++ = val;
- *ptr++ = val >> 8;
- *ptr++ = val >> 16;
- *ptr++ = val >> 24;
-}
-
/******************** Messages and Errors ***********************************/
diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
index f08e8946384..6e0f56701e4 100644
--- a/tools/usb/testusb.c
+++ b/tools/usb/testusb.c
@@ -3,7 +3,7 @@
/*
* Copyright (c) 2002 by David Brownell
* Copyright (c) 2010 by Samsung Electronics
- * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ * Author: Michal Nazarewicz <mina86@mina86.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
diff --git a/tools/virtio/linux/hrtimer.h b/tools/virtio/linux/hrtimer.h
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/tools/virtio/linux/hrtimer.h
diff --git a/tools/virtio/linux/module.h b/tools/virtio/linux/module.h
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/tools/virtio/linux/module.h
diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h
index b4fbc91c41b..7579f19e61e 100644
--- a/tools/virtio/linux/virtio.h
+++ b/tools/virtio/linux/virtio.h
@@ -181,6 +181,9 @@ struct virtqueue {
#define smp_mb() mb()
# define smp_rmb() barrier()
# define smp_wmb() barrier()
+/* Weak barriers should be used. If not - it's a bug */
+# define rmb() abort()
+# define wmb() abort()
#else
#error Please fill in barrier macros
#endif
diff --git a/tools/vm/Makefile b/tools/vm/Makefile
new file mode 100644
index 00000000000..8e30e5c40f8
--- /dev/null
+++ b/tools/vm/Makefile
@@ -0,0 +1,11 @@
+# Makefile for vm tools
+
+CC = $(CROSS_COMPILE)gcc
+CFLAGS = -Wall -Wextra
+
+all: page-types slabinfo
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+clean:
+ $(RM) page-types slabinfo
diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c
new file mode 100644
index 00000000000..7dab7b25b5c
--- /dev/null
+++ b/tools/vm/page-types.c
@@ -0,0 +1,1102 @@
+/*
+ * page-types: Tool for querying page flags
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should find a copy of v2 of the GNU General Public License somewhere on
+ * your Linux system; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Copyright (C) 2009 Intel corporation
+ *
+ * Authors: Wu Fengguang <fengguang.wu@intel.com>
+ */
+
+#define _LARGEFILE64_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <getopt.h>
+#include <limits.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/mount.h>
+#include <sys/statfs.h>
+#include "../../include/linux/magic.h"
+
+
+#ifndef MAX_PATH
+# define MAX_PATH 256
+#endif
+
+#ifndef STR
+# define _STR(x) #x
+# define STR(x) _STR(x)
+#endif
+
+/*
+ * pagemap kernel ABI bits
+ */
+
+#define PM_ENTRY_BYTES sizeof(uint64_t)
+#define PM_STATUS_BITS 3
+#define PM_STATUS_OFFSET (64 - PM_STATUS_BITS)
+#define PM_STATUS_MASK (((1LL << PM_STATUS_BITS) - 1) << PM_STATUS_OFFSET)
+#define PM_STATUS(nr) (((nr) << PM_STATUS_OFFSET) & PM_STATUS_MASK)
+#define PM_PSHIFT_BITS 6
+#define PM_PSHIFT_OFFSET (PM_STATUS_OFFSET - PM_PSHIFT_BITS)
+#define PM_PSHIFT_MASK (((1LL << PM_PSHIFT_BITS) - 1) << PM_PSHIFT_OFFSET)
+#define PM_PSHIFT(x) (((u64) (x) << PM_PSHIFT_OFFSET) & PM_PSHIFT_MASK)
+#define PM_PFRAME_MASK ((1LL << PM_PSHIFT_OFFSET) - 1)
+#define PM_PFRAME(x) ((x) & PM_PFRAME_MASK)
+
+#define PM_PRESENT PM_STATUS(4LL)
+#define PM_SWAP PM_STATUS(2LL)
+
+
+/*
+ * kernel page flags
+ */
+
+#define KPF_BYTES 8
+#define PROC_KPAGEFLAGS "/proc/kpageflags"
+
+/* copied from kpageflags_read() */
+#define KPF_LOCKED 0
+#define KPF_ERROR 1
+#define KPF_REFERENCED 2
+#define KPF_UPTODATE 3
+#define KPF_DIRTY 4
+#define KPF_LRU 5
+#define KPF_ACTIVE 6
+#define KPF_SLAB 7
+#define KPF_WRITEBACK 8
+#define KPF_RECLAIM 9
+#define KPF_BUDDY 10
+
+/* [11-20] new additions in 2.6.31 */
+#define KPF_MMAP 11
+#define KPF_ANON 12
+#define KPF_SWAPCACHE 13
+#define KPF_SWAPBACKED 14
+#define KPF_COMPOUND_HEAD 15
+#define KPF_COMPOUND_TAIL 16
+#define KPF_HUGE 17
+#define KPF_UNEVICTABLE 18
+#define KPF_HWPOISON 19
+#define KPF_NOPAGE 20
+#define KPF_KSM 21
+#define KPF_THP 22
+
+/* [32-] kernel hacking assistances */
+#define KPF_RESERVED 32
+#define KPF_MLOCKED 33
+#define KPF_MAPPEDTODISK 34
+#define KPF_PRIVATE 35
+#define KPF_PRIVATE_2 36
+#define KPF_OWNER_PRIVATE 37
+#define KPF_ARCH 38
+#define KPF_UNCACHED 39
+
+/* [48-] take some arbitrary free slots for expanding overloaded flags
+ * not part of kernel API
+ */
+#define KPF_READAHEAD 48
+#define KPF_SLOB_FREE 49
+#define KPF_SLUB_FROZEN 50
+#define KPF_SLUB_DEBUG 51
+
+#define KPF_ALL_BITS ((uint64_t)~0ULL)
+#define KPF_HACKERS_BITS (0xffffULL << 32)
+#define KPF_OVERLOADED_BITS (0xffffULL << 48)
+#define BIT(name) (1ULL << KPF_##name)
+#define BITS_COMPOUND (BIT(COMPOUND_HEAD) | BIT(COMPOUND_TAIL))
+
+static const char * const page_flag_names[] = {
+ [KPF_LOCKED] = "L:locked",
+ [KPF_ERROR] = "E:error",
+ [KPF_REFERENCED] = "R:referenced",
+ [KPF_UPTODATE] = "U:uptodate",
+ [KPF_DIRTY] = "D:dirty",
+ [KPF_LRU] = "l:lru",
+ [KPF_ACTIVE] = "A:active",
+ [KPF_SLAB] = "S:slab",
+ [KPF_WRITEBACK] = "W:writeback",
+ [KPF_RECLAIM] = "I:reclaim",
+ [KPF_BUDDY] = "B:buddy",
+
+ [KPF_MMAP] = "M:mmap",
+ [KPF_ANON] = "a:anonymous",
+ [KPF_SWAPCACHE] = "s:swapcache",
+ [KPF_SWAPBACKED] = "b:swapbacked",
+ [KPF_COMPOUND_HEAD] = "H:compound_head",
+ [KPF_COMPOUND_TAIL] = "T:compound_tail",
+ [KPF_HUGE] = "G:huge",
+ [KPF_UNEVICTABLE] = "u:unevictable",
+ [KPF_HWPOISON] = "X:hwpoison",
+ [KPF_NOPAGE] = "n:nopage",
+ [KPF_KSM] = "x:ksm",
+ [KPF_THP] = "t:thp",
+
+ [KPF_RESERVED] = "r:reserved",
+ [KPF_MLOCKED] = "m:mlocked",
+ [KPF_MAPPEDTODISK] = "d:mappedtodisk",
+ [KPF_PRIVATE] = "P:private",
+ [KPF_PRIVATE_2] = "p:private_2",
+ [KPF_OWNER_PRIVATE] = "O:owner_private",
+ [KPF_ARCH] = "h:arch",
+ [KPF_UNCACHED] = "c:uncached",
+
+ [KPF_READAHEAD] = "I:readahead",
+ [KPF_SLOB_FREE] = "P:slob_free",
+ [KPF_SLUB_FROZEN] = "A:slub_frozen",
+ [KPF_SLUB_DEBUG] = "E:slub_debug",
+};
+
+
+static const char * const debugfs_known_mountpoints[] = {
+ "/sys/kernel/debug",
+ "/debug",
+ 0,
+};
+
+/*
+ * data structures
+ */
+
+static int opt_raw; /* for kernel developers */
+static int opt_list; /* list pages (in ranges) */
+static int opt_no_summary; /* don't show summary */
+static pid_t opt_pid; /* process to walk */
+
+#define MAX_ADDR_RANGES 1024
+static int nr_addr_ranges;
+static unsigned long opt_offset[MAX_ADDR_RANGES];
+static unsigned long opt_size[MAX_ADDR_RANGES];
+
+#define MAX_VMAS 10240
+static int nr_vmas;
+static unsigned long pg_start[MAX_VMAS];
+static unsigned long pg_end[MAX_VMAS];
+
+#define MAX_BIT_FILTERS 64
+static int nr_bit_filters;
+static uint64_t opt_mask[MAX_BIT_FILTERS];
+static uint64_t opt_bits[MAX_BIT_FILTERS];
+
+static int page_size;
+
+static int pagemap_fd;
+static int kpageflags_fd;
+
+static int opt_hwpoison;
+static int opt_unpoison;
+
+static char hwpoison_debug_fs[MAX_PATH+1];
+static int hwpoison_inject_fd;
+static int hwpoison_forget_fd;
+
+#define HASH_SHIFT 13
+#define HASH_SIZE (1 << HASH_SHIFT)
+#define HASH_MASK (HASH_SIZE - 1)
+#define HASH_KEY(flags) (flags & HASH_MASK)
+
+static unsigned long total_pages;
+static unsigned long nr_pages[HASH_SIZE];
+static uint64_t page_flags[HASH_SIZE];
+
+
+/*
+ * helper functions
+ */
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define min_t(type, x, y) ({ \
+ type __min1 = (x); \
+ type __min2 = (y); \
+ __min1 < __min2 ? __min1 : __min2; })
+
+#define max_t(type, x, y) ({ \
+ type __max1 = (x); \
+ type __max2 = (y); \
+ __max1 > __max2 ? __max1 : __max2; })
+
+static unsigned long pages2mb(unsigned long pages)
+{
+ return (pages * page_size) >> 20;
+}
+
+static void fatal(const char *x, ...)
+{
+ va_list ap;
+
+ va_start(ap, x);
+ vfprintf(stderr, x, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+static int checked_open(const char *pathname, int flags)
+{
+ int fd = open(pathname, flags);
+
+ if (fd < 0) {
+ perror(pathname);
+ exit(EXIT_FAILURE);
+ }
+
+ return fd;
+}
+
+/*
+ * pagemap/kpageflags routines
+ */
+
+static unsigned long do_u64_read(int fd, char *name,
+ uint64_t *buf,
+ unsigned long index,
+ unsigned long count)
+{
+ long bytes;
+
+ if (index > ULONG_MAX / 8)
+ fatal("index overflow: %lu\n", index);
+
+ if (lseek(fd, index * 8, SEEK_SET) < 0) {
+ perror(name);
+ exit(EXIT_FAILURE);
+ }
+
+ bytes = read(fd, buf, count * 8);
+ if (bytes < 0) {
+ perror(name);
+ exit(EXIT_FAILURE);
+ }
+ if (bytes % 8)
+ fatal("partial read: %lu bytes\n", bytes);
+
+ return bytes / 8;
+}
+
+static unsigned long kpageflags_read(uint64_t *buf,
+ unsigned long index,
+ unsigned long pages)
+{
+ return do_u64_read(kpageflags_fd, PROC_KPAGEFLAGS, buf, index, pages);
+}
+
+static unsigned long pagemap_read(uint64_t *buf,
+ unsigned long index,
+ unsigned long pages)
+{
+ return do_u64_read(pagemap_fd, "/proc/pid/pagemap", buf, index, pages);
+}
+
+static unsigned long pagemap_pfn(uint64_t val)
+{
+ unsigned long pfn;
+
+ if (val & PM_PRESENT)
+ pfn = PM_PFRAME(val);
+ else
+ pfn = 0;
+
+ return pfn;
+}
+
+
+/*
+ * page flag names
+ */
+
+static char *page_flag_name(uint64_t flags)
+{
+ static char buf[65];
+ int present;
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) {
+ present = (flags >> i) & 1;
+ if (!page_flag_names[i]) {
+ if (present)
+ fatal("unknown flag bit %d\n", i);
+ continue;
+ }
+ buf[j++] = present ? page_flag_names[i][0] : '_';
+ }
+
+ return buf;
+}
+
+static char *page_flag_longname(uint64_t flags)
+{
+ static char buf[1024];
+ int i, n;
+
+ for (i = 0, n = 0; i < ARRAY_SIZE(page_flag_names); i++) {
+ if (!page_flag_names[i])
+ continue;
+ if ((flags >> i) & 1)
+ n += snprintf(buf + n, sizeof(buf) - n, "%s,",
+ page_flag_names[i] + 2);
+ }
+ if (n)
+ n--;
+ buf[n] = '\0';
+
+ return buf;
+}
+
+
+/*
+ * page list and summary
+ */
+
+static void show_page_range(unsigned long voffset,
+ unsigned long offset, uint64_t flags)
+{
+ static uint64_t flags0;
+ static unsigned long voff;
+ static unsigned long index;
+ static unsigned long count;
+
+ if (flags == flags0 && offset == index + count &&
+ (!opt_pid || voffset == voff + count)) {
+ count++;
+ return;
+ }
+
+ if (count) {
+ if (opt_pid)
+ printf("%lx\t", voff);
+ printf("%lx\t%lx\t%s\n",
+ index, count, page_flag_name(flags0));
+ }
+
+ flags0 = flags;
+ index = offset;
+ voff = voffset;
+ count = 1;
+}
+
+static void show_page(unsigned long voffset,
+ unsigned long offset, uint64_t flags)
+{
+ if (opt_pid)
+ printf("%lx\t", voffset);
+ printf("%lx\t%s\n", offset, page_flag_name(flags));
+}
+
+static void show_summary(void)
+{
+ int i;
+
+ printf(" flags\tpage-count MB"
+ " symbolic-flags\t\t\tlong-symbolic-flags\n");
+
+ for (i = 0; i < ARRAY_SIZE(nr_pages); i++) {
+ if (nr_pages[i])
+ printf("0x%016llx\t%10lu %8lu %s\t%s\n",
+ (unsigned long long)page_flags[i],
+ nr_pages[i],
+ pages2mb(nr_pages[i]),
+ page_flag_name(page_flags[i]),
+ page_flag_longname(page_flags[i]));
+ }
+
+ printf(" total\t%10lu %8lu\n",
+ total_pages, pages2mb(total_pages));
+}
+
+
+/*
+ * page flag filters
+ */
+
+static int bit_mask_ok(uint64_t flags)
+{
+ int i;
+
+ for (i = 0; i < nr_bit_filters; i++) {
+ if (opt_bits[i] == KPF_ALL_BITS) {
+ if ((flags & opt_mask[i]) == 0)
+ return 0;
+ } else {
+ if ((flags & opt_mask[i]) != opt_bits[i])
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static uint64_t expand_overloaded_flags(uint64_t flags)
+{
+ /* SLOB/SLUB overload several page flags */
+ if (flags & BIT(SLAB)) {
+ if (flags & BIT(PRIVATE))
+ flags ^= BIT(PRIVATE) | BIT(SLOB_FREE);
+ if (flags & BIT(ACTIVE))
+ flags ^= BIT(ACTIVE) | BIT(SLUB_FROZEN);
+ if (flags & BIT(ERROR))
+ flags ^= BIT(ERROR) | BIT(SLUB_DEBUG);
+ }
+
+ /* PG_reclaim is overloaded as PG_readahead in the read path */
+ if ((flags & (BIT(RECLAIM) | BIT(WRITEBACK))) == BIT(RECLAIM))
+ flags ^= BIT(RECLAIM) | BIT(READAHEAD);
+
+ return flags;
+}
+
+static uint64_t well_known_flags(uint64_t flags)
+{
+ /* hide flags intended only for kernel hacker */
+ flags &= ~KPF_HACKERS_BITS;
+
+ /* hide non-hugeTLB compound pages */
+ if ((flags & BITS_COMPOUND) && !(flags & BIT(HUGE)))
+ flags &= ~BITS_COMPOUND;
+
+ return flags;
+}
+
+static uint64_t kpageflags_flags(uint64_t flags)
+{
+ flags = expand_overloaded_flags(flags);
+
+ if (!opt_raw)
+ flags = well_known_flags(flags);
+
+ return flags;
+}
+
+/* verify that a mountpoint is actually a debugfs instance */
+static int debugfs_valid_mountpoint(const char *debugfs)
+{
+ struct statfs st_fs;
+
+ if (statfs(debugfs, &st_fs) < 0)
+ return -ENOENT;
+ else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
+ return -ENOENT;
+
+ return 0;
+}
+
+/* find the path to the mounted debugfs */
+static const char *debugfs_find_mountpoint(void)
+{
+ const char **ptr;
+ char type[100];
+ FILE *fp;
+
+ ptr = debugfs_known_mountpoints;
+ while (*ptr) {
+ if (debugfs_valid_mountpoint(*ptr) == 0) {
+ strcpy(hwpoison_debug_fs, *ptr);
+ return hwpoison_debug_fs;
+ }
+ ptr++;
+ }
+
+ /* give up and parse /proc/mounts */
+ fp = fopen("/proc/mounts", "r");
+ if (fp == NULL)
+ perror("Can't open /proc/mounts for read");
+
+ while (fscanf(fp, "%*s %"
+ STR(MAX_PATH)
+ "s %99s %*s %*d %*d\n",
+ hwpoison_debug_fs, type) == 2) {
+ if (strcmp(type, "debugfs") == 0)
+ break;
+ }
+ fclose(fp);
+
+ if (strcmp(type, "debugfs") != 0)
+ return NULL;
+
+ return hwpoison_debug_fs;
+}
+
+/* mount the debugfs somewhere if it's not mounted */
+
+static void debugfs_mount(void)
+{
+ const char **ptr;
+
+ /* see if it's already mounted */
+ if (debugfs_find_mountpoint())
+ return;
+
+ ptr = debugfs_known_mountpoints;
+ while (*ptr) {
+ if (mount(NULL, *ptr, "debugfs", 0, NULL) == 0) {
+ /* save the mountpoint */
+ strcpy(hwpoison_debug_fs, *ptr);
+ break;
+ }
+ ptr++;
+ }
+
+ if (*ptr == NULL) {
+ perror("mount debugfs");
+ exit(EXIT_FAILURE);
+ }
+}
+
+/*
+ * page actions
+ */
+
+static void prepare_hwpoison_fd(void)
+{
+ char buf[MAX_PATH + 1];
+
+ debugfs_mount();
+
+ if (opt_hwpoison && !hwpoison_inject_fd) {
+ snprintf(buf, MAX_PATH, "%s/hwpoison/corrupt-pfn",
+ hwpoison_debug_fs);
+ hwpoison_inject_fd = checked_open(buf, O_WRONLY);
+ }
+
+ if (opt_unpoison && !hwpoison_forget_fd) {
+ snprintf(buf, MAX_PATH, "%s/hwpoison/unpoison-pfn",
+ hwpoison_debug_fs);
+ hwpoison_forget_fd = checked_open(buf, O_WRONLY);
+ }
+}
+
+static int hwpoison_page(unsigned long offset)
+{
+ char buf[100];
+ int len;
+
+ len = sprintf(buf, "0x%lx\n", offset);
+ len = write(hwpoison_inject_fd, buf, len);
+ if (len < 0) {
+ perror("hwpoison inject");
+ return len;
+ }
+ return 0;
+}
+
+static int unpoison_page(unsigned long offset)
+{
+ char buf[100];
+ int len;
+
+ len = sprintf(buf, "0x%lx\n", offset);
+ len = write(hwpoison_forget_fd, buf, len);
+ if (len < 0) {
+ perror("hwpoison forget");
+ return len;
+ }
+ return 0;
+}
+
+/*
+ * page frame walker
+ */
+
+static int hash_slot(uint64_t flags)
+{
+ int k = HASH_KEY(flags);
+ int i;
+
+ /* Explicitly reserve slot 0 for flags 0: the following logic
+ * cannot distinguish an unoccupied slot from slot (flags==0).
+ */
+ if (flags == 0)
+ return 0;
+
+ /* search through the remaining (HASH_SIZE-1) slots */
+ for (i = 1; i < ARRAY_SIZE(page_flags); i++, k++) {
+ if (!k || k >= ARRAY_SIZE(page_flags))
+ k = 1;
+ if (page_flags[k] == 0) {
+ page_flags[k] = flags;
+ return k;
+ }
+ if (page_flags[k] == flags)
+ return k;
+ }
+
+ fatal("hash table full: bump up HASH_SHIFT?\n");
+ exit(EXIT_FAILURE);
+}
+
+static void add_page(unsigned long voffset,
+ unsigned long offset, uint64_t flags)
+{
+ flags = kpageflags_flags(flags);
+
+ if (!bit_mask_ok(flags))
+ return;
+
+ if (opt_hwpoison)
+ hwpoison_page(offset);
+ if (opt_unpoison)
+ unpoison_page(offset);
+
+ if (opt_list == 1)
+ show_page_range(voffset, offset, flags);
+ else if (opt_list == 2)
+ show_page(voffset, offset, flags);
+
+ nr_pages[hash_slot(flags)]++;
+ total_pages++;
+}
+
+#define KPAGEFLAGS_BATCH (64 << 10) /* 64k pages */
+static void walk_pfn(unsigned long voffset,
+ unsigned long index,
+ unsigned long count)
+{
+ uint64_t buf[KPAGEFLAGS_BATCH];
+ unsigned long batch;
+ long pages;
+ unsigned long i;
+
+ while (count) {
+ batch = min_t(unsigned long, count, KPAGEFLAGS_BATCH);
+ pages = kpageflags_read(buf, index, batch);
+ if (pages == 0)
+ break;
+
+ for (i = 0; i < pages; i++)
+ add_page(voffset + i, index + i, buf[i]);
+
+ index += pages;
+ count -= pages;
+ }
+}
+
+#define PAGEMAP_BATCH (64 << 10)
+static void walk_vma(unsigned long index, unsigned long count)
+{
+ uint64_t buf[PAGEMAP_BATCH];
+ unsigned long batch;
+ unsigned long pages;
+ unsigned long pfn;
+ unsigned long i;
+
+ while (count) {
+ batch = min_t(unsigned long, count, PAGEMAP_BATCH);
+ pages = pagemap_read(buf, index, batch);
+ if (pages == 0)
+ break;
+
+ for (i = 0; i < pages; i++) {
+ pfn = pagemap_pfn(buf[i]);
+ if (pfn)
+ walk_pfn(index + i, pfn, 1);
+ }
+
+ index += pages;
+ count -= pages;
+ }
+}
+
+static void walk_task(unsigned long index, unsigned long count)
+{
+ const unsigned long end = index + count;
+ unsigned long start;
+ int i = 0;
+
+ while (index < end) {
+
+ while (pg_end[i] <= index)
+ if (++i >= nr_vmas)
+ return;
+ if (pg_start[i] >= end)
+ return;
+
+ start = max_t(unsigned long, pg_start[i], index);
+ index = min_t(unsigned long, pg_end[i], end);
+
+ assert(start < index);
+ walk_vma(start, index - start);
+ }
+}
+
+static void add_addr_range(unsigned long offset, unsigned long size)
+{
+ if (nr_addr_ranges >= MAX_ADDR_RANGES)
+ fatal("too many addr ranges\n");
+
+ opt_offset[nr_addr_ranges] = offset;
+ opt_size[nr_addr_ranges] = min_t(unsigned long, size, ULONG_MAX-offset);
+ nr_addr_ranges++;
+}
+
+static void walk_addr_ranges(void)
+{
+ int i;
+
+ kpageflags_fd = checked_open(PROC_KPAGEFLAGS, O_RDONLY);
+
+ if (!nr_addr_ranges)
+ add_addr_range(0, ULONG_MAX);
+
+ for (i = 0; i < nr_addr_ranges; i++)
+ if (!opt_pid)
+ walk_pfn(0, opt_offset[i], opt_size[i]);
+ else
+ walk_task(opt_offset[i], opt_size[i]);
+
+ close(kpageflags_fd);
+}
+
+
+/*
+ * user interface
+ */
+
+static const char *page_flag_type(uint64_t flag)
+{
+ if (flag & KPF_HACKERS_BITS)
+ return "(r)";
+ if (flag & KPF_OVERLOADED_BITS)
+ return "(o)";
+ return " ";
+}
+
+static void usage(void)
+{
+ int i, j;
+
+ printf(
+"page-types [options]\n"
+" -r|--raw Raw mode, for kernel developers\n"
+" -d|--describe flags Describe flags\n"
+" -a|--addr addr-spec Walk a range of pages\n"
+" -b|--bits bits-spec Walk pages with specified bits\n"
+" -p|--pid pid Walk process address space\n"
+#if 0 /* planned features */
+" -f|--file filename Walk file address space\n"
+#endif
+" -l|--list Show page details in ranges\n"
+" -L|--list-each Show page details one by one\n"
+" -N|--no-summary Don't show summary info\n"
+" -X|--hwpoison hwpoison pages\n"
+" -x|--unpoison unpoison pages\n"
+" -h|--help Show this usage message\n"
+"flags:\n"
+" 0x10 bitfield format, e.g.\n"
+" anon bit-name, e.g.\n"
+" 0x10,anon comma-separated list, e.g.\n"
+"addr-spec:\n"
+" N one page at offset N (unit: pages)\n"
+" N+M pages range from N to N+M-1\n"
+" N,M pages range from N to M-1\n"
+" N, pages range from N to end\n"
+" ,M pages range from 0 to M-1\n"
+"bits-spec:\n"
+" bit1,bit2 (flags & (bit1|bit2)) != 0\n"
+" bit1,bit2=bit1 (flags & (bit1|bit2)) == bit1\n"
+" bit1,~bit2 (flags & (bit1|bit2)) == bit1\n"
+" =bit1,bit2 flags == (bit1|bit2)\n"
+"bit-names:\n"
+ );
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) {
+ if (!page_flag_names[i])
+ continue;
+ printf("%16s%s", page_flag_names[i] + 2,
+ page_flag_type(1ULL << i));
+ if (++j > 3) {
+ j = 0;
+ putchar('\n');
+ }
+ }
+ printf("\n "
+ "(r) raw mode bits (o) overloaded bits\n");
+}
+
+static unsigned long long parse_number(const char *str)
+{
+ unsigned long long n;
+
+ n = strtoll(str, NULL, 0);
+
+ if (n == 0 && str[0] != '0')
+ fatal("invalid name or number: %s\n", str);
+
+ return n;
+}
+
+static void parse_pid(const char *str)
+{
+ FILE *file;
+ char buf[5000];
+
+ opt_pid = parse_number(str);
+
+ sprintf(buf, "/proc/%d/pagemap", opt_pid);
+ pagemap_fd = checked_open(buf, O_RDONLY);
+
+ sprintf(buf, "/proc/%d/maps", opt_pid);
+ file = fopen(buf, "r");
+ if (!file) {
+ perror(buf);
+ exit(EXIT_FAILURE);
+ }
+
+ while (fgets(buf, sizeof(buf), file) != NULL) {
+ unsigned long vm_start;
+ unsigned long vm_end;
+ unsigned long long pgoff;
+ int major, minor;
+ char r, w, x, s;
+ unsigned long ino;
+ int n;
+
+ n = sscanf(buf, "%lx-%lx %c%c%c%c %llx %x:%x %lu",
+ &vm_start,
+ &vm_end,
+ &r, &w, &x, &s,
+ &pgoff,
+ &major, &minor,
+ &ino);
+ if (n < 10) {
+ fprintf(stderr, "unexpected line: %s\n", buf);
+ continue;
+ }
+ pg_start[nr_vmas] = vm_start / page_size;
+ pg_end[nr_vmas] = vm_end / page_size;
+ if (++nr_vmas >= MAX_VMAS) {
+ fprintf(stderr, "too many VMAs\n");
+ break;
+ }
+ }
+ fclose(file);
+}
+
+static void parse_file(const char *name)
+{
+}
+
+static void parse_addr_range(const char *optarg)
+{
+ unsigned long offset;
+ unsigned long size;
+ char *p;
+
+ p = strchr(optarg, ',');
+ if (!p)
+ p = strchr(optarg, '+');
+
+ if (p == optarg) {
+ offset = 0;
+ size = parse_number(p + 1);
+ } else if (p) {
+ offset = parse_number(optarg);
+ if (p[1] == '\0')
+ size = ULONG_MAX;
+ else {
+ size = parse_number(p + 1);
+ if (*p == ',') {
+ if (size < offset)
+ fatal("invalid range: %lu,%lu\n",
+ offset, size);
+ size -= offset;
+ }
+ }
+ } else {
+ offset = parse_number(optarg);
+ size = 1;
+ }
+
+ add_addr_range(offset, size);
+}
+
+static void add_bits_filter(uint64_t mask, uint64_t bits)
+{
+ if (nr_bit_filters >= MAX_BIT_FILTERS)
+ fatal("too much bit filters\n");
+
+ opt_mask[nr_bit_filters] = mask;
+ opt_bits[nr_bit_filters] = bits;
+ nr_bit_filters++;
+}
+
+static uint64_t parse_flag_name(const char *str, int len)
+{
+ int i;
+
+ if (!*str || !len)
+ return 0;
+
+ if (len <= 8 && !strncmp(str, "compound", len))
+ return BITS_COMPOUND;
+
+ for (i = 0; i < ARRAY_SIZE(page_flag_names); i++) {
+ if (!page_flag_names[i])
+ continue;
+ if (!strncmp(str, page_flag_names[i] + 2, len))
+ return 1ULL << i;
+ }
+
+ return parse_number(str);
+}
+
+static uint64_t parse_flag_names(const char *str, int all)
+{
+ const char *p = str;
+ uint64_t flags = 0;
+
+ while (1) {
+ if (*p == ',' || *p == '=' || *p == '\0') {
+ if ((*str != '~') || (*str == '~' && all && *++str))
+ flags |= parse_flag_name(str, p - str);
+ if (*p != ',')
+ break;
+ str = p + 1;
+ }
+ p++;
+ }
+
+ return flags;
+}
+
+static void parse_bits_mask(const char *optarg)
+{
+ uint64_t mask;
+ uint64_t bits;
+ const char *p;
+
+ p = strchr(optarg, '=');
+ if (p == optarg) {
+ mask = KPF_ALL_BITS;
+ bits = parse_flag_names(p + 1, 0);
+ } else if (p) {
+ mask = parse_flag_names(optarg, 0);
+ bits = parse_flag_names(p + 1, 0);
+ } else if (strchr(optarg, '~')) {
+ mask = parse_flag_names(optarg, 1);
+ bits = parse_flag_names(optarg, 0);
+ } else {
+ mask = parse_flag_names(optarg, 0);
+ bits = KPF_ALL_BITS;
+ }
+
+ add_bits_filter(mask, bits);
+}
+
+static void describe_flags(const char *optarg)
+{
+ uint64_t flags = parse_flag_names(optarg, 0);
+
+ printf("0x%016llx\t%s\t%s\n",
+ (unsigned long long)flags,
+ page_flag_name(flags),
+ page_flag_longname(flags));
+}
+
+static const struct option opts[] = {
+ { "raw" , 0, NULL, 'r' },
+ { "pid" , 1, NULL, 'p' },
+ { "file" , 1, NULL, 'f' },
+ { "addr" , 1, NULL, 'a' },
+ { "bits" , 1, NULL, 'b' },
+ { "describe" , 1, NULL, 'd' },
+ { "list" , 0, NULL, 'l' },
+ { "list-each" , 0, NULL, 'L' },
+ { "no-summary", 0, NULL, 'N' },
+ { "hwpoison" , 0, NULL, 'X' },
+ { "unpoison" , 0, NULL, 'x' },
+ { "help" , 0, NULL, 'h' },
+ { NULL , 0, NULL, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int c;
+
+ page_size = getpagesize();
+
+ while ((c = getopt_long(argc, argv,
+ "rp:f:a:b:d:lLNXxh", opts, NULL)) != -1) {
+ switch (c) {
+ case 'r':
+ opt_raw = 1;
+ break;
+ case 'p':
+ parse_pid(optarg);
+ break;
+ case 'f':
+ parse_file(optarg);
+ break;
+ case 'a':
+ parse_addr_range(optarg);
+ break;
+ case 'b':
+ parse_bits_mask(optarg);
+ break;
+ case 'd':
+ describe_flags(optarg);
+ exit(0);
+ case 'l':
+ opt_list = 1;
+ break;
+ case 'L':
+ opt_list = 2;
+ break;
+ case 'N':
+ opt_no_summary = 1;
+ break;
+ case 'X':
+ opt_hwpoison = 1;
+ prepare_hwpoison_fd();
+ break;
+ case 'x':
+ opt_unpoison = 1;
+ prepare_hwpoison_fd();
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if (opt_list && opt_pid)
+ printf("voffset\t");
+ if (opt_list == 1)
+ printf("offset\tlen\tflags\n");
+ if (opt_list == 2)
+ printf("offset\tflags\n");
+
+ walk_addr_ranges();
+
+ if (opt_list == 1)
+ show_page_range(0, 0, 0); /* drain the buffer */
+
+ if (opt_no_summary)
+ return 0;
+
+ if (opt_list)
+ printf("\n\n");
+
+ show_summary();
+
+ return 0;
+}
diff --git a/tools/slub/slabinfo.c b/tools/vm/slabinfo.c
index 164cbcf6110..164cbcf6110 100644
--- a/tools/slub/slabinfo.c
+++ b/tools/vm/slabinfo.c