1
0
Fork 0

Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull perf fixes from Ingo Molnar:
 "Tooling fixes, the biggest patch is one that decouples the kernel's
  list.h from tooling list.h"

* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (23 commits)
  perf tools: Fallback to srcdir/Documentation/tips.txt
  perf ui/tui: Print helpline message as is
  perf tools: Set and pass DOCDIR to builtin-report.c
  perf tools: Add file_only config option to strlist
  perf tools: Add more usage tips
  perf record: Add --buildid-all option
  tools subcmd: Add missing NORETURN define for parse-options.h
  tools: Fix formatting of the "make -C tools" help message
  tools: Make list.h self-sufficient
  perf tools: Fix mmap2 event allocation in synthesize code
  perf stat: Fix recort_usage typo
  perf test: Reset err after using it hold errcode in hist testcases
  perf test: Fix false TEST_OK result for 'perf test hist'
  tools build: Add BPF feature check to test-all
  perf bpf: Fix build breakage due to libbpf
  tools: Move Makefile.arch from perf/config to tools/scripts
  perf tools: Fix PowerPC native building
  perf tools: Fix phony build target for build-test
  perf tools: Add -lutil in python lib list for broken python-config
  perf tools: Add missing sources to perf's MANIFEST
  ...
hifive-unleashed-5.1
Linus Torvalds 2016-01-14 11:39:09 -08:00
commit 747a9b0a08
28 changed files with 918 additions and 66 deletions

View File

@ -8,24 +8,24 @@ include scripts/Makefile.include
help: help:
@echo 'Possible targets:' @echo 'Possible targets:'
@echo '' @echo ''
@echo ' acpi - ACPI tools' @echo ' acpi - ACPI tools'
@echo ' cgroup - cgroup tools' @echo ' cgroup - cgroup tools'
@echo ' cpupower - a tool for all things x86 CPU power' @echo ' cpupower - a tool for all things x86 CPU power'
@echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer'
@echo ' hv - tools used when in Hyper-V clients' @echo ' freefall - laptop accelerometer program for disk protection'
@echo ' iio - IIO tools' @echo ' hv - tools used when in Hyper-V clients'
@echo ' lguest - a minimal 32-bit x86 hypervisor' @echo ' iio - IIO tools'
@echo ' perf - Linux performance measurement and analysis tool' @echo ' lguest - a minimal 32-bit x86 hypervisor'
@echo ' selftests - various kernel selftests' @echo ' net - misc networking tools'
@echo ' spi - spi tools' @echo ' perf - Linux performance measurement and analysis tool'
@echo ' turbostat - Intel CPU idle stats and freq reporting tool' @echo ' selftests - various kernel selftests'
@echo ' usb - USB testing tools' @echo ' spi - spi tools'
@echo ' virtio - vhost test module' @echo ' tmon - thermal monitoring and tuning tool'
@echo ' net - misc networking tools' @echo ' turbostat - Intel CPU idle stats and freq reporting tool'
@echo ' vm - misc vm tools' @echo ' usb - USB testing tools'
@echo ' virtio - vhost test module'
@echo ' vm - misc vm tools'
@echo ' x86_energy_perf_policy - Intel energy policy tool' @echo ' x86_energy_perf_policy - Intel energy policy tool'
@echo ' tmon - thermal monitoring and tuning tool'
@echo ' freefall - laptop accelerometer program for disk protection'
@echo '' @echo ''
@echo 'You can do:' @echo 'You can do:'
@echo ' $$ make -C tools/ <tool>_install' @echo ' $$ make -C tools/ <tool>_install'
@ -128,6 +128,12 @@ liblockdep_clean:
libapi_clean: libapi_clean:
$(call descend,lib/api,clean) $(call descend,lib/api,clean)
libbpf_clean:
$(call descend,lib/bpf,clean)
libsubcmd_clean:
$(call descend,lib/subcmd,clean)
perf_clean: perf_clean:
$(call descend,$(@:_clean=),clean) $(call descend,$(@:_clean=),clean)
@ -143,9 +149,12 @@ tmon_clean:
freefall_clean: freefall_clean:
$(call descend,laptop/freefall,clean) $(call descend,laptop/freefall,clean)
build_clean:
$(call descend,build,clean)
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean
.PHONY: FORCE .PHONY: FORCE

View File

@ -125,6 +125,10 @@
# include "test-get_cpuid.c" # include "test-get_cpuid.c"
#undef main #undef main
#define main main_test_bpf
# include "test-bpf.c"
#undef main
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
main_test_libpython(); main_test_libpython();
@ -153,6 +157,7 @@ int main(int argc, char *argv[])
main_test_pthread_attr_setaffinity_np(); main_test_pthread_attr_setaffinity_np();
main_test_lzma(); main_test_lzma();
main_test_get_cpuid(); main_test_get_cpuid();
main_test_bpf();
return 0; return 0;
} }

View File

@ -1,9 +1,23 @@
#include <asm/unistd.h>
#include <linux/bpf.h> #include <linux/bpf.h>
#include <unistd.h>
#ifndef __NR_bpf
# if defined(__i386__)
# define __NR_bpf 357
# elif defined(__x86_64__)
# define __NR_bpf 321
# elif defined(__aarch64__)
# define __NR_bpf 280
# error __NR_bpf not defined. libbpf does not support your arch.
# endif
#endif
int main(void) int main(void)
{ {
union bpf_attr attr; union bpf_attr attr;
/* Check fields in attr */
attr.prog_type = BPF_PROG_TYPE_KPROBE; attr.prog_type = BPF_PROG_TYPE_KPROBE;
attr.insn_cnt = 0; attr.insn_cnt = 0;
attr.insns = 0; attr.insns = 0;
@ -14,5 +28,9 @@ int main(void)
attr.kern_version = 0; attr.kern_version = 0;
attr = attr; attr = attr;
return 0; /*
* Test existence of __NR_bpf and BPF_PROG_LOAD.
* This call should fail if we run the testcase.
*/
return syscall(__NR_bpf, BPF_PROG_LOAD, attr, sizeof(attr));
} }

View File

@ -1,11 +1,751 @@
#include <linux/compiler.h> #ifndef __TOOLS_LINUX_LIST_H
#include <linux/kernel.h> #define __TOOLS_LINUX_LIST_H
#include <linux/types.h> #include <linux/types.h>
#include <linux/poison.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include "../../../include/linux/list.h" /*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
#ifndef CONFIG_DEBUG_LIST
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
#else
extern void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next);
#endif
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
WRITE_ONCE(prev->next, next);
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
#ifndef CONFIG_DEBUG_LIST
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
#else
extern void __list_del_entry(struct list_head *entry);
extern void list_del(struct list_head *entry);
#endif
/**
* list_replace - replace old entry by new one
* @old : the element to be replaced
* @new : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace(struct list_head *old,
struct list_head *new)
{
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}
static inline void list_replace_init(struct list_head *old,
struct list_head *new)
{
list_replace(old, new);
INIT_LIST_HEAD(old);
}
/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}
/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_empty_careful - tests whether a list is empty and not being modified
* @head: the list to test
*
* Description:
* tests whether a list is empty _and_ checks that no other CPU might be
* in the process of modifying either member (next or prev)
*
* NOTE: using list_empty_careful() without synchronization
* can only be safe if the only activity that can happen
* to the list entry is list_del_init(). Eg. it cannot be used
* if another CPU could re-list_add() it.
*/
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
/**
* list_rotate_left - rotate the list to the left
* @head: the head of the list
*/
static inline void list_rotate_left(struct list_head *head)
{
struct list_head *first;
if (!list_empty(head)) {
first = head->next;
list_move_tail(first, head);
}
}
/**
* list_is_singular - tests whether a list has just one entry.
* @head: the list to test.
*/
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
struct list_head *new_first = entry->next;
list->next = head->next;
list->next->prev = list;
list->prev = entry;
entry->next = list;
head->next = new_first;
new_first->prev = head;
}
/**
* list_cut_position - cut a list into two
* @list: a new list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
* and if so we won't cut the list
*
* This helper moves the initial part of @head, up to and
* including @entry, from @head to @list. You should
* pass on @entry an element you know is on @head. @list
* should be an empty list or a list you do not care about
* losing its data.
*
*/
static inline void list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
if (list_empty(head))
return;
if (list_is_singular(head) &&
(head->next != entry && head != entry))
return;
if (entry == head)
INIT_LIST_HEAD(list);
else
__list_cut_position(list, head, entry);
}
static inline void __list_splice(const struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
/**
* list_splice - join two lists, this is designed for stacks
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(const struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head, head->next);
}
/**
* list_splice_tail - join two lists, each list being a queue
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice_tail(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head->prev, head);
}
/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head, head->next);
INIT_LIST_HEAD(list);
}
}
/**
* list_splice_tail_init - join two lists and reinitialise the emptied list
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* Each of the lists is a queue.
* The list at @list is reinitialised
*/
static inline void list_splice_tail_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_last_entry - get the last element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
/**
* list_first_entry_or_null - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note that if the list is empty, it returns NULL.
*/
#define list_first_entry_or_null(ptr, type, member) \
(!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
/**
* list_prev_entry - get the prev element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, typeof(*(pos)), member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
pos != (head); \
pos = n, n = pos->prev)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
/**
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
* @pos: the type * to use as a start point
* @head: the head of the list
* @member: the name of the list_head within the struct.
*
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
*/
#define list_prepare_entry(pos, head, member) \
((pos) ? : list_entry(head, typeof(*pos), member))
/**
* list_for_each_entry_continue - continue iteration over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Continue to iterate over list of given type, continuing after
* the current position.
*/
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_next_entry(pos, member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_continue_reverse - iterate backwards from the given point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Start to iterate over list of given type backwards, continuing after
* the current position.
*/
#define list_for_each_entry_continue_reverse(pos, head, member) \
for (pos = list_prev_entry(pos, member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
/**
* list_for_each_entry_from - iterate over list of given type from the current point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing from current position.
*/
#define list_for_each_entry_from(pos, head, member) \
for (; &pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_continue - continue list iteration safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing after current point,
* safe against removal of list entry.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_next_entry(pos, member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_from - iterate over list from current point safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type from current point, safe against
* removal of list entry.
*/
#define list_for_each_entry_safe_from(pos, n, head, member) \
for (n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate backwards over list of given type, safe against removal
* of list entry.
*/
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member), \
n = list_prev_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_prev_entry(n, member))
/**
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
* @pos: the loop cursor used in the list_for_each_entry_safe loop
* @n: temporary storage used in list_for_each_entry_safe
* @member: the name of the list_head within the struct.
*
* list_safe_reset_next is not safe to use in general if the list may be
* modified concurrently (eg. the lock is dropped in the loop body). An
* exception to this is if the cursor element (pos) is pinned in the list,
* and list_safe_reset_next is called after re-taking the lock and before
* completing the current iteration of the loop body.
*/
#define list_safe_reset_next(pos, n, member) \
n = list_next_entry(pos, member)
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*/
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
WRITE_ONCE(*pprev, next);
if (next)
next->pprev = pprev;
}
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
*(n->pprev) = n;
}
static inline void hlist_add_behind(struct hlist_node *n,
struct hlist_node *prev)
{
n->next = prev->next;
prev->next = n;
n->pprev = &prev->next;
if (n->next)
n->next->pprev = &n->next;
}
/* after that we'll appear to be on some hlist and hlist_del will work */
static inline void hlist_add_fake(struct hlist_node *n)
{
n->pprev = &n->next;
}
static inline bool hlist_fake(struct hlist_node *h)
{
return h->pprev == &h->next;
}
/*
* Move a list from one list head to another. Fixup the pprev
* reference of the first entry if it exists.
*/
static inline void hlist_move_list(struct hlist_head *old,
struct hlist_head *new)
{
new->first = old->first;
if (new->first)
new->first->pprev = &new->first;
old->first = NULL;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos ; pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
#define hlist_entry_safe(ptr, type, member) \
({ typeof(ptr) ____ptr = (ptr); \
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
})
/**
* hlist_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(pos, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_continue - iterate over a hlist continuing after current point
* @pos: the type * to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_continue(pos, member) \
for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_from - iterate over a hlist continuing from current point
* @pos: the type * to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_from(pos, member) \
for (; pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(pos, n, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
pos && ({ n = pos->member.next; 1; }); \
pos = hlist_entry_safe(n, typeof(*pos), member))
#ifndef TOOLS_LIST_H
#define TOOLS_LIST_H
/** /**
* list_del_range - deletes range of entries from list. * list_del_range - deletes range of entries from list.
* @begin: first element in the range to delete from the list. * @begin: first element in the range to delete from the list.
@ -27,4 +767,5 @@ static inline void list_del_range(struct list_head *begin,
*/ */
#define list_for_each_from(pos, head) \ #define list_for_each_from(pos, head) \
for (; pos != (head); pos = pos->next) for (; pos != (head); pos = pos->next)
#endif
#endif /* __TOOLS_LINUX_LIST_H */

View File

@ -6,6 +6,12 @@ BPF_EXTRAVERSION = 1
MAKEFLAGS += --no-print-directory MAKEFLAGS += --no-print-directory
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
#$(info Determined 'srctree' to be $(srctree))
endif
# Makefiles suck: This macro sets a default value of $(2) for the # Makefiles suck: This macro sets a default value of $(2) for the
# variable named by $(1), unless the variable has been set by # variable named by $(1), unless the variable has been set by
@ -31,7 +37,8 @@ INSTALL = install
DESTDIR ?= DESTDIR ?=
DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1) include $(srctree)/tools/scripts/Makefile.arch
ifeq ($(LP64), 1) ifeq ($(LP64), 1)
libdir_relative = lib64 libdir_relative = lib64
else else
@ -57,13 +64,6 @@ ifndef VERBOSE
VERBOSE = 0 VERBOSE = 0
endif endif
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
#$(info Determined 'srctree' to be $(srctree))
endif
FEATURE_USER = .libbpf FEATURE_USER = .libbpf
FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf
FEATURE_DISPLAY = libelf bpf FEATURE_DISPLAY = libelf bpf
@ -192,7 +192,7 @@ config-clean:
$(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
clean: clean:
$(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \ $(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d .*.cmd \
$(RM) LIBBPF-CFLAGS $(RM) LIBBPF-CFLAGS
$(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP.libbpf $(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP.libbpf

View File

@ -14,8 +14,8 @@
#include "bpf.h" #include "bpf.h"
/* /*
* When building perf, unistd.h is override. Define __NR_bpf is * When building perf, unistd.h is overrided. __NR_bpf is
* required to be defined. * required to be defined explicitly.
*/ */
#ifndef __NR_bpf #ifndef __NR_bpf
# if defined(__i386__) # if defined(__i386__)

View File

@ -149,7 +149,7 @@ install_lib: all_cmd
install: install_lib install: install_lib
clean: clean:
$(RM) *.o *~ $(TARGETS) *.a *liblockdep*.so* $(VERSION_FILES) .*.d $(RM) *.o *~ $(TARGETS) *.a *liblockdep*.so* $(VERSION_FILES) .*.d .*.cmd
$(RM) tags TAGS $(RM) tags TAGS
PHONY += force PHONY += force

View File

@ -4,6 +4,10 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#ifndef NORETURN
#define NORETURN __attribute__((__noreturn__))
#endif
enum parse_opt_type { enum parse_opt_type {
/* special types */ /* special types */
OPTION_END, OPTION_END,

View File

@ -42,6 +42,7 @@ CFLAGS_perf.o += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))" \
-include $(OUTPUT)PERF-VERSION-FILE -include $(OUTPUT)PERF-VERSION-FILE
CFLAGS_builtin-trace.o += -DSTRACE_GROUPS_DIR="BUILD_STR($(STRACE_GROUPS_DIR_SQ))" CFLAGS_builtin-trace.o += -DSTRACE_GROUPS_DIR="BUILD_STR($(STRACE_GROUPS_DIR_SQ))"
CFLAGS_builtin-report.o += -DTIPDIR="BUILD_STR($(tipdir_SQ))" CFLAGS_builtin-report.o += -DTIPDIR="BUILD_STR($(tipdir_SQ))"
CFLAGS_builtin-report.o += -DDOCDIR="BUILD_STR($(srcdir_SQ)/Documentation)"
libperf-y += util/ libperf-y += util/
libperf-y += arch/ libperf-y += arch/

View File

@ -338,6 +338,9 @@ Options passed to clang when compiling BPF scriptlets.
Specify vmlinux path which has debuginfo. Specify vmlinux path which has debuginfo.
(enabled when BPF prologue is on) (enabled when BPF prologue is on)
--buildid-all::
Record build-id of all DSOs regardless whether it's actually hit or not.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-stat[1], linkperf:perf-list[1] linkperf:perf-stat[1], linkperf:perf-list[1]

View File

@ -12,3 +12,18 @@ List events using substring match: perf list <keyword>
To see list of saved events and attributes: perf evlist -v To see list of saved events and attributes: perf evlist -v
Use --symfs <dir> if your symbol files are in non-standard locations Use --symfs <dir> if your symbol files are in non-standard locations
To see callchains in a more compact form: perf report -g folded To see callchains in a more compact form: perf report -g folded
Show individual samples with: perf script
Limit to show entries above 5% only: perf report --percent-limit 5
Profiling branch (mis)predictions with: perf record -b / perf report
Treat branches as callchains: perf report --branch-history
To count events in every 1000 msec: perf stat -I 1000
Print event counts in CSV format with: perf stat -x,
If you have debuginfo enabled, try: perf report -s sym,srcline
For memory address profiling, try: perf mem record / perf mem report
For tracepoint events, try: perf report -s trace_fields
To record callchains for each sample: perf record -g
To record every process run by an user: perf record -u <user>
Skip collecing build-id when recording: perf record -B
To change sampling frequency to 100 Hz: perf record -F 100
See assembly instructions with percentage: perf annotate <symbol>
If you prefer Intel style assembly, try: perf annotate -M intel

View File

@ -28,6 +28,7 @@ tools/lib/string.c
tools/lib/symbol/kallsyms.c tools/lib/symbol/kallsyms.c
tools/lib/symbol/kallsyms.h tools/lib/symbol/kallsyms.h
tools/lib/find_bit.c tools/lib/find_bit.c
tools/lib/bitmap.c
tools/include/asm/atomic.h tools/include/asm/atomic.h
tools/include/asm/barrier.h tools/include/asm/barrier.h
tools/include/asm/bug.h tools/include/asm/bug.h
@ -57,6 +58,7 @@ tools/include/linux/rbtree_augmented.h
tools/include/linux/string.h tools/include/linux/string.h
tools/include/linux/types.h tools/include/linux/types.h
tools/include/linux/err.h tools/include/linux/err.h
tools/include/linux/bitmap.h
include/asm-generic/bitops/arch_hweight.h include/asm-generic/bitops/arch_hweight.h
include/asm-generic/bitops/const_hweight.h include/asm-generic/bitops/const_hweight.h
include/asm-generic/bitops/fls64.h include/asm-generic/bitops/fls64.h

View File

@ -50,6 +50,7 @@ struct record {
int realtime_prio; int realtime_prio;
bool no_buildid; bool no_buildid;
bool no_buildid_cache; bool no_buildid_cache;
bool buildid_all;
unsigned long long samples; unsigned long long samples;
}; };
@ -362,6 +363,13 @@ static int process_buildids(struct record *rec)
*/ */
symbol_conf.ignore_vmlinux_buildid = true; symbol_conf.ignore_vmlinux_buildid = true;
/*
* If --buildid-all is given, it marks all DSO regardless of hits,
* so no need to process samples.
*/
if (rec->buildid_all)
rec->tool.sample = NULL;
return perf_session__process_events(session); return perf_session__process_events(session);
} }
@ -756,12 +764,8 @@ out_child:
if (!rec->no_buildid) { if (!rec->no_buildid) {
process_buildids(rec); process_buildids(rec);
/*
* We take all buildids when the file contains if (rec->buildid_all)
* AUX area tracing data because we do not decode the
* trace because it would take too long.
*/
if (rec->opts.full_auxtrace)
dsos__hit_all(rec->session); dsos__hit_all(rec->session);
} }
perf_session__write_header(rec->session, rec->evlist, fd, true); perf_session__write_header(rec->session, rec->evlist, fd, true);
@ -1138,6 +1142,8 @@ struct option __record_options[] = {
"options passed to clang when compiling BPF scriptlets"), "options passed to clang when compiling BPF scriptlets"),
OPT_STRING(0, "vmlinux", &symbol_conf.vmlinux_name, OPT_STRING(0, "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"), "file", "vmlinux pathname"),
OPT_BOOLEAN(0, "buildid-all", &record.buildid_all,
"Record build-id of all DSOs regardless of hits"),
OPT_END() OPT_END()
}; };
@ -1255,6 +1261,14 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
if (err) if (err)
goto out_symbol_exit; goto out_symbol_exit;
/*
* We take all buildids when the file contains
* AUX area tracing data because we do not decode the
* trace because it would take too long.
*/
if (rec->opts.full_auxtrace)
rec->buildid_all = true;
if (record_opts__config(&rec->opts)) { if (record_opts__config(&rec->opts)) {
err = -EINVAL; err = -EINVAL;
goto out_symbol_exit; goto out_symbol_exit;

View File

@ -28,6 +28,7 @@
#include "util/tool.h" #include "util/tool.h"
#include <subcmd/parse-options.h> #include <subcmd/parse-options.h>
#include <subcmd/exec-cmd.h>
#include "util/parse-events.h" #include "util/parse-events.h"
#include "util/thread.h" #include "util/thread.h"
@ -433,7 +434,14 @@ static int report__browse_hists(struct report *rep)
int ret; int ret;
struct perf_session *session = rep->session; struct perf_session *session = rep->session;
struct perf_evlist *evlist = session->evlist; struct perf_evlist *evlist = session->evlist;
const char *help = perf_tip(TIPDIR); const char *help = perf_tip(system_path(TIPDIR));
if (help == NULL) {
/* fallback for people who don't install perf ;-) */
help = perf_tip(DOCDIR);
if (help == NULL)
help = "Cannot load tips.txt file, please install perf!";
}
switch (use_browser) { switch (use_browser) {
case 1: case 1:

View File

@ -1588,7 +1588,7 @@ static int add_default_attributes(void)
return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs); return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs);
} }
static const char * const recort_usage[] = { static const char * const stat_record_usage[] = {
"perf stat record [<options>]", "perf stat record [<options>]",
NULL, NULL,
}; };
@ -1611,7 +1611,7 @@ static int __cmd_record(int argc, const char **argv)
struct perf_session *session; struct perf_session *session;
struct perf_data_file *file = &perf_stat.file; struct perf_data_file *file = &perf_stat.file;
argc = parse_options(argc, argv, stat_options, record_usage, argc = parse_options(argc, argv, stat_options, stat_record_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
if (output_name) if (output_name)
@ -1745,7 +1745,7 @@ int process_cpu_map_event(struct perf_tool *tool __maybe_unused,
return set_maps(st); return set_maps(st);
} }
static const char * const report_usage[] = { static const char * const stat_report_usage[] = {
"perf stat report [<options>]", "perf stat report [<options>]",
NULL, NULL,
}; };
@ -1779,7 +1779,7 @@ static int __cmd_report(int argc, const char **argv)
struct stat st; struct stat st;
int ret; int ret;
argc = parse_options(argc, argv, options, report_usage, 0); argc = parse_options(argc, argv, options, stat_report_usage, 0);
if (!input_name || !strlen(input_name)) { if (!input_name || !strlen(input_name)) {
if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))

View File

@ -17,7 +17,7 @@ detected_var = $(shell echo "$(1)=$($(1))" >> $(OUTPUT).config-detected)
CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS) CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS)
include $(src-perf)/config/Makefile.arch include $(srctree)/tools/scripts/Makefile.arch
$(call detected_var,ARCH) $(call detected_var,ARCH)
@ -493,7 +493,7 @@ else
PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null)
PYTHON_EMBED_LDFLAGS := $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) PYTHON_EMBED_LDFLAGS := $(call strip-libs,$(PYTHON_EMBED_LDOPTS))
PYTHON_EMBED_LIBADD := $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) PYTHON_EMBED_LIBADD := $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) -lutil
PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null)
FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS)
@ -692,6 +692,7 @@ template_dir = share/perf-core/templates
STRACE_GROUPS_DIR = share/perf-core/strace/groups STRACE_GROUPS_DIR = share/perf-core/strace/groups
htmldir = share/doc/perf-doc htmldir = share/doc/perf-doc
tipdir = share/doc/perf-tip tipdir = share/doc/perf-tip
srcdir = $(srctree)/tools/perf
ifeq ($(prefix),/usr) ifeq ($(prefix),/usr)
sysconfdir = /etc sysconfdir = /etc
ETC_PERFCONFIG = $(sysconfdir)/perfconfig ETC_PERFCONFIG = $(sysconfdir)/perfconfig
@ -722,6 +723,7 @@ tipdir_SQ = $(subst ','\'',$(tipdir))
prefix_SQ = $(subst ','\'',$(prefix)) prefix_SQ = $(subst ','\'',$(prefix))
sysconfdir_SQ = $(subst ','\'',$(sysconfdir)) sysconfdir_SQ = $(subst ','\'',$(sysconfdir))
libdir_SQ = $(subst ','\'',$(libdir)) libdir_SQ = $(subst ','\'',$(libdir))
srcdir_SQ = $(subst ','\'',$(srcdir))
ifneq ($(filter /%,$(firstword $(perfexecdir))),) ifneq ($(filter /%,$(firstword $(perfexecdir))),)
perfexec_instdir = $(perfexecdir) perfexec_instdir = $(perfexecdir)
@ -776,6 +778,7 @@ $(call detected_var,STRACE_GROUPS_DIR_SQ)
$(call detected_var,prefix_SQ) $(call detected_var,prefix_SQ)
$(call detected_var,perfexecdir_SQ) $(call detected_var,perfexecdir_SQ)
$(call detected_var,tipdir_SQ) $(call detected_var,tipdir_SQ)
$(call detected_var,srcdir_SQ)
$(call detected_var,LIBDIR) $(call detected_var,LIBDIR)
$(call detected_var,GTK_CFLAGS) $(call detected_var,GTK_CFLAGS)
$(call detected_var,PERL_EMBED_CCOPTS) $(call detected_var,PERL_EMBED_CCOPTS)

View File

@ -87,11 +87,6 @@ struct machine *setup_fake_machine(struct machines *machines)
return NULL; return NULL;
} }
if (machine__create_kernel_maps(machine)) {
pr_debug("Cannot create kernel maps\n");
return NULL;
}
for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { for (i = 0; i < ARRAY_SIZE(fake_threads); i++) {
struct thread *thread; struct thread *thread;

View File

@ -706,6 +706,7 @@ int test__hists_cumulate(int subtest __maybe_unused)
err = parse_events(evlist, "cpu-clock", NULL); err = parse_events(evlist, "cpu-clock", NULL);
if (err) if (err)
goto out; goto out;
err = TEST_FAIL;
machines__init(&machines); machines__init(&machines);

View File

@ -120,6 +120,7 @@ int test__hists_filter(int subtest __maybe_unused)
err = parse_events(evlist, "task-clock", NULL); err = parse_events(evlist, "task-clock", NULL);
if (err) if (err)
goto out; goto out;
err = TEST_FAIL;
/* default sort order (comm,dso,sym) will be used */ /* default sort order (comm,dso,sym) will be used */
if (setup_sorting(NULL) < 0) if (setup_sorting(NULL) < 0)

View File

@ -293,6 +293,7 @@ int test__hists_link(int subtest __maybe_unused)
if (err) if (err)
goto out; goto out;
err = TEST_FAIL;
/* default sort order (comm,dso,sym) will be used */ /* default sort order (comm,dso,sym) will be used */
if (setup_sorting(NULL) < 0) if (setup_sorting(NULL) < 0)
goto out; goto out;

View File

@ -597,6 +597,7 @@ int test__hists_output(int subtest __maybe_unused)
err = parse_events(evlist, "cpu-clock", NULL); err = parse_events(evlist, "cpu-clock", NULL);
if (err) if (err)
goto out; goto out;
err = TEST_FAIL;
machines__init(&machines); machines__init(&machines);

View File

@ -1,3 +1,5 @@
include ../scripts/Makefile.include
ifndef MK ifndef MK
ifeq ($(MAKECMDGOALS),) ifeq ($(MAKECMDGOALS),)
# no target specified, trigger the whole suite # no target specified, trigger the whole suite
@ -12,7 +14,19 @@ endif
else else
PERF := . PERF := .
include config/Makefile.arch # As per kernel Makefile, avoid funny character set dependencies
unexport LC_ALL
LC_COLLATE=C
LC_NUMERIC=C
export LC_COLLATE LC_NUMERIC
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
#$(info Determined 'srctree' to be $(srctree))
endif
include $(srctree)/tools/scripts/Makefile.arch
# FIXME looks like x86 is the only arch running tests ;-) # FIXME looks like x86 is the only arch running tests ;-)
# we need some IS_(32/64) flag to make this generic # we need some IS_(32/64) flag to make this generic
@ -280,5 +294,5 @@ all: $(run) $(run_O) tarpkg make_kernelsrc make_kernelsrc_tools
out: $(run_O) out: $(run_O)
@echo OK @echo OK
.PHONY: all $(run) $(run_O) tarpkg clean .PHONY: all $(run) $(run_O) tarpkg clean make_kernelsrc make_kernelsrc_tools
endif # ifndef MK endif # ifndef MK

View File

@ -480,7 +480,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *help)
hists__browser_title(browser->hists, hbt, title, sizeof(title)); hists__browser_title(browser->hists, hbt, title, sizeof(title));
if (ui_browser__show(&browser->b, title, help) < 0) if (ui_browser__show(&browser->b, title, "%s", help) < 0)
return -1; return -1;
while (1) { while (1) {

View File

@ -503,7 +503,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
if (comm_event == NULL) if (comm_event == NULL)
goto out; goto out;
mmap_event = malloc(sizeof(mmap_event->mmap) + machine->id_hdr_size); mmap_event = malloc(sizeof(mmap_event->mmap2) + machine->id_hdr_size);
if (mmap_event == NULL) if (mmap_event == NULL)
goto out_free_comm; goto out_free_comm;
@ -577,7 +577,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
if (comm_event == NULL) if (comm_event == NULL)
goto out; goto out;
mmap_event = malloc(sizeof(mmap_event->mmap) + machine->id_hdr_size); mmap_event = malloc(sizeof(mmap_event->mmap2) + machine->id_hdr_size);
if (mmap_event == NULL) if (mmap_event == NULL)
goto out_free_comm; goto out_free_comm;

View File

@ -126,6 +126,11 @@ static int strlist__parse_list_entry(struct strlist *slist, const char *s,
err = strlist__load(slist, subst); err = strlist__load(slist, subst);
goto out; goto out;
} }
if (slist->file_only) {
err = -ENOENT;
goto out;
}
} }
err = strlist__add(slist, s); err = strlist__add(slist, s);
@ -157,11 +162,13 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf
if (slist != NULL) { if (slist != NULL) {
bool dupstr = true; bool dupstr = true;
bool file_only = false;
const char *dirname = NULL; const char *dirname = NULL;
if (config) { if (config) {
dupstr = !config->dont_dupstr; dupstr = !config->dont_dupstr;
dirname = config->dirname; dirname = config->dirname;
file_only = config->file_only;
} }
rblist__init(&slist->rblist); rblist__init(&slist->rblist);
@ -170,6 +177,7 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf
slist->rblist.node_delete = strlist__node_delete; slist->rblist.node_delete = strlist__node_delete;
slist->dupstr = dupstr; slist->dupstr = dupstr;
slist->file_only = file_only;
if (list && strlist__parse_list(slist, list, dirname) != 0) if (list && strlist__parse_list(slist, list, dirname) != 0)
goto out_error; goto out_error;

View File

@ -13,11 +13,18 @@ struct str_node {
struct strlist { struct strlist {
struct rblist rblist; struct rblist rblist;
bool dupstr; bool dupstr;
bool file_only;
}; };
/*
* @file_only: When dirname is present, only consider entries as filenames,
* that should not be added to the list if dirname/entry is not
* found
*/
struct strlist_config { struct strlist_config {
bool dont_dupstr; bool dont_dupstr;
bool file_only;
const char *dirname; const char *dirname;
}; };

View File

@ -17,7 +17,6 @@
#include <unistd.h> #include <unistd.h>
#include "callchain.h" #include "callchain.h"
#include "strlist.h" #include "strlist.h"
#include <subcmd/exec-cmd.h>
struct callchain_param callchain_param = { struct callchain_param callchain_param = {
.mode = CHAIN_GRAPH_ABS, .mode = CHAIN_GRAPH_ABS,
@ -672,14 +671,16 @@ const char *perf_tip(const char *dirpath)
struct str_node *node; struct str_node *node;
char *tip = NULL; char *tip = NULL;
struct strlist_config conf = { struct strlist_config conf = {
.dirname = system_path(dirpath) , .dirname = dirpath,
.file_only = true,
}; };
tips = strlist__new("tips.txt", &conf); tips = strlist__new("tips.txt", &conf);
if (tips == NULL || strlist__nr_entries(tips) == 1) { if (tips == NULL)
tip = (char *)"Cannot find tips.txt file"; return errno == ENOENT ? NULL : "Tip: get more memory! ;-p";
if (strlist__nr_entries(tips) == 0)
goto out; goto out;
}
node = strlist__entry(tips, random() % strlist__nr_entries(tips)); node = strlist__entry(tips, random() % strlist__nr_entries(tips));
if (asprintf(&tip, "Tip: %s", node->s) < 0) if (asprintf(&tip, "Tip: %s", node->s) < 0)