aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/perf_counter.h24
-rw-r--r--kernel/perf_counter.c145
-rw-r--r--mm/mmap.c10
3 files changed, 177 insertions, 2 deletions
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h
index 8ac18852dcfe..037a81145aca 100644
--- a/include/linux/perf_counter.h
+++ b/include/linux/perf_counter.h
@@ -137,9 +137,11 @@ struct perf_counter_hw_event {
137 exclude_kernel : 1, /* ditto kernel */ 137 exclude_kernel : 1, /* ditto kernel */
138 exclude_hv : 1, /* ditto hypervisor */ 138 exclude_hv : 1, /* ditto hypervisor */
139 exclude_idle : 1, /* don't count when idle */ 139 exclude_idle : 1, /* don't count when idle */
140 include_tid : 1, /* include the tid */ 140 include_tid : 1, /* include the tid */
141 mmap : 1, /* include mmap data */
142 munmap : 1, /* include munmap data */
141 143
142 __reserved_1 : 54; 144 __reserved_1 : 52;
143 145
144 __u32 extra_config_len; 146 __u32 extra_config_len;
145 __u32 __reserved_4; 147 __u32 __reserved_4;
@@ -211,6 +213,9 @@ enum perf_event_type {
211 PERF_EVENT_IP = 0, 213 PERF_EVENT_IP = 0,
212 PERF_EVENT_GROUP = 1, 214 PERF_EVENT_GROUP = 1,
213 215
216 PERF_EVENT_MMAP = 2,
217 PERF_EVENT_MUNMAP = 3,
218
214 __PERF_EVENT_TID = 0x100, 219 __PERF_EVENT_TID = 0x100,
215}; 220};
216 221
@@ -491,6 +496,12 @@ static inline int is_software_counter(struct perf_counter *counter)
491 496
492extern void perf_swcounter_event(u32, u64, int, struct pt_regs *); 497extern void perf_swcounter_event(u32, u64, int, struct pt_regs *);
493 498
499extern void perf_counter_mmap(unsigned long addr, unsigned long len,
500 unsigned long pgoff, struct file *file);
501
502extern void perf_counter_munmap(unsigned long addr, unsigned long len,
503 unsigned long pgoff, struct file *file);
504
494#else 505#else
495static inline void 506static inline void
496perf_counter_task_sched_in(struct task_struct *task, int cpu) { } 507perf_counter_task_sched_in(struct task_struct *task, int cpu) { }
@@ -511,6 +522,15 @@ static inline int perf_counter_task_enable(void) { return -EINVAL; }
511static inline void 522static inline void
512perf_swcounter_event(u32 event, u64 nr, int nmi, struct pt_regs *regs) { } 523perf_swcounter_event(u32 event, u64 nr, int nmi, struct pt_regs *regs) { }
513 524
525
526static inline void
527perf_counter_mmap(unsigned long addr, unsigned long len,
528 unsigned long pgoff, struct file *file) { }
529
530static inline void
531perf_counter_munmap(unsigned long addr, unsigned long len,
532 unsigned long pgoff, struct file *file) { }
533
514#endif 534#endif
515 535
516#endif /* __KERNEL__ */ 536#endif /* __KERNEL__ */
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c
index c95e92329b97..f35e89e3d6a4 100644
--- a/kernel/perf_counter.c
+++ b/kernel/perf_counter.c
@@ -25,6 +25,7 @@
25#include <linux/anon_inodes.h> 25#include <linux/anon_inodes.h>
26#include <linux/kernel_stat.h> 26#include <linux/kernel_stat.h>
27#include <linux/perf_counter.h> 27#include <linux/perf_counter.h>
28#include <linux/dcache.h>
28 29
29#include <asm/irq_regs.h> 30#include <asm/irq_regs.h>
30 31
@@ -1844,6 +1845,150 @@ void perf_counter_output(struct perf_counter *counter,
1844} 1845}
1845 1846
1846/* 1847/*
1848 * mmap tracking
1849 */
1850
1851struct perf_mmap_event {
1852 struct file *file;
1853 char *file_name;
1854 int file_size;
1855
1856 struct {
1857 struct perf_event_header header;
1858
1859 u32 pid;
1860 u32 tid;
1861 u64 start;
1862 u64 len;
1863 u64 pgoff;
1864 } event;
1865};
1866
1867static void perf_counter_mmap_output(struct perf_counter *counter,
1868 struct perf_mmap_event *mmap_event)
1869{
1870 struct perf_output_handle handle;
1871 int size = mmap_event->event.header.size;
1872 int ret = perf_output_begin(&handle, counter, size);
1873
1874 if (ret)
1875 return;
1876
1877 perf_output_put(&handle, mmap_event->event);
1878 perf_output_copy(&handle, mmap_event->file_name,
1879 mmap_event->file_size);
1880 perf_output_end(&handle, 0);
1881}
1882
1883static int perf_counter_mmap_match(struct perf_counter *counter,
1884 struct perf_mmap_event *mmap_event)
1885{
1886 if (counter->hw_event.mmap &&
1887 mmap_event->event.header.type == PERF_EVENT_MMAP)
1888 return 1;
1889
1890 if (counter->hw_event.munmap &&
1891 mmap_event->event.header.type == PERF_EVENT_MUNMAP)
1892 return 1;
1893
1894 return 0;
1895}
1896
1897static void perf_counter_mmap_ctx(struct perf_counter_context *ctx,
1898 struct perf_mmap_event *mmap_event)
1899{
1900 struct perf_counter *counter;
1901
1902 if (system_state != SYSTEM_RUNNING || list_empty(&ctx->event_list))
1903 return;
1904
1905 rcu_read_lock();
1906 list_for_each_entry_rcu(counter, &ctx->event_list, event_entry) {
1907 if (perf_counter_mmap_match(counter, mmap_event))
1908 perf_counter_mmap_output(counter, mmap_event);
1909 }
1910 rcu_read_unlock();
1911}
1912
1913static void perf_counter_mmap_event(struct perf_mmap_event *mmap_event)
1914{
1915 struct perf_cpu_context *cpuctx;
1916 struct file *file = mmap_event->file;
1917 unsigned int size;
1918 char tmp[16];
1919 char *buf = NULL;
1920 char *name;
1921
1922 if (file) {
1923 buf = kzalloc(PATH_MAX, GFP_KERNEL);
1924 if (!buf) {
1925 name = strncpy(tmp, "//enomem", sizeof(tmp));
1926 goto got_name;
1927 }
1928 name = dentry_path(file->f_dentry, buf, PATH_MAX);
1929 if (IS_ERR(name)) {
1930 name = strncpy(tmp, "//toolong", sizeof(tmp));
1931 goto got_name;
1932 }
1933 } else {
1934 name = strncpy(tmp, "//anon", sizeof(tmp));
1935 goto got_name;
1936 }
1937
1938got_name:
1939 size = ALIGN(strlen(name), sizeof(u64));
1940
1941 mmap_event->file_name = name;
1942 mmap_event->file_size = size;
1943
1944 mmap_event->event.header.size = sizeof(mmap_event->event) + size;
1945
1946 cpuctx = &get_cpu_var(perf_cpu_context);
1947 perf_counter_mmap_ctx(&cpuctx->ctx, mmap_event);
1948 put_cpu_var(perf_cpu_context);
1949
1950 perf_counter_mmap_ctx(&current->perf_counter_ctx, mmap_event);
1951
1952 kfree(buf);
1953}
1954
1955void perf_counter_mmap(unsigned long addr, unsigned long len,
1956 unsigned long pgoff, struct file *file)
1957{
1958 struct perf_mmap_event mmap_event = {
1959 .file = file,
1960 .event = {
1961 .header = { .type = PERF_EVENT_MMAP, },
1962 .pid = current->group_leader->pid,
1963 .tid = current->pid,
1964 .start = addr,
1965 .len = len,
1966 .pgoff = pgoff,
1967 },
1968 };
1969
1970 perf_counter_mmap_event(&mmap_event);
1971}
1972
1973void perf_counter_munmap(unsigned long addr, unsigned long len,
1974 unsigned long pgoff, struct file *file)
1975{
1976 struct perf_mmap_event mmap_event = {
1977 .file = file,
1978 .event = {
1979 .header = { .type = PERF_EVENT_MUNMAP, },
1980 .pid = current->group_leader->pid,
1981 .tid = current->pid,
1982 .start = addr,
1983 .len = len,
1984 .pgoff = pgoff,
1985 },
1986 };
1987
1988 perf_counter_mmap_event(&mmap_event);
1989}
1990
1991/*
1847 * Generic software counter infrastructure 1992 * Generic software counter infrastructure
1848 */ 1993 */
1849 1994
diff --git a/mm/mmap.c b/mm/mmap.c
index 4a3841186c11..1df63f614f97 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -28,6 +28,7 @@
28#include <linux/mempolicy.h> 28#include <linux/mempolicy.h>
29#include <linux/rmap.h> 29#include <linux/rmap.h>
30#include <linux/mmu_notifier.h> 30#include <linux/mmu_notifier.h>
31#include <linux/perf_counter.h>
31 32
32#include <asm/uaccess.h> 33#include <asm/uaccess.h>
33#include <asm/cacheflush.h> 34#include <asm/cacheflush.h>
@@ -1223,6 +1224,9 @@ munmap_back:
1223 if (correct_wcount) 1224 if (correct_wcount)
1224 atomic_inc(&inode->i_writecount); 1225 atomic_inc(&inode->i_writecount);
1225out: 1226out:
1227 if (vm_flags & VM_EXEC)
1228 perf_counter_mmap(addr, len, pgoff, file);
1229
1226 mm->total_vm += len >> PAGE_SHIFT; 1230 mm->total_vm += len >> PAGE_SHIFT;
1227 vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT); 1231 vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
1228 if (vm_flags & VM_LOCKED) { 1232 if (vm_flags & VM_LOCKED) {
@@ -1756,6 +1760,12 @@ static void remove_vma_list(struct mm_struct *mm, struct vm_area_struct *vma)
1756 do { 1760 do {
1757 long nrpages = vma_pages(vma); 1761 long nrpages = vma_pages(vma);
1758 1762
1763 if (vma->vm_flags & VM_EXEC) {
1764 perf_counter_munmap(vma->vm_start,
1765 nrpages << PAGE_SHIFT,
1766 vma->vm_pgoff, vma->vm_file);
1767 }
1768
1759 mm->total_vm -= nrpages; 1769 mm->total_vm -= nrpages;
1760 vm_stat_account(mm, vma->vm_flags, vma->vm_file, -nrpages); 1770 vm_stat_account(mm, vma->vm_flags, vma->vm_file, -nrpages);
1761 vma = remove_vma(vma); 1771 vma = remove_vma(vma);