diff options
-rw-r--r-- | include/linux/perf_counter.h | 24 | ||||
-rw-r--r-- | kernel/perf_counter.c | 145 | ||||
-rw-r--r-- | mm/mmap.c | 10 |
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 | ||
492 | extern void perf_swcounter_event(u32, u64, int, struct pt_regs *); | 497 | extern void perf_swcounter_event(u32, u64, int, struct pt_regs *); |
493 | 498 | ||
499 | extern void perf_counter_mmap(unsigned long addr, unsigned long len, | ||
500 | unsigned long pgoff, struct file *file); | ||
501 | |||
502 | extern void perf_counter_munmap(unsigned long addr, unsigned long len, | ||
503 | unsigned long pgoff, struct file *file); | ||
504 | |||
494 | #else | 505 | #else |
495 | static inline void | 506 | static inline void |
496 | perf_counter_task_sched_in(struct task_struct *task, int cpu) { } | 507 | perf_counter_task_sched_in(struct task_struct *task, int cpu) { } |
@@ -511,6 +522,15 @@ static inline int perf_counter_task_enable(void) { return -EINVAL; } | |||
511 | static inline void | 522 | static inline void |
512 | perf_swcounter_event(u32 event, u64 nr, int nmi, struct pt_regs *regs) { } | 523 | perf_swcounter_event(u32 event, u64 nr, int nmi, struct pt_regs *regs) { } |
513 | 524 | ||
525 | |||
526 | static inline void | ||
527 | perf_counter_mmap(unsigned long addr, unsigned long len, | ||
528 | unsigned long pgoff, struct file *file) { } | ||
529 | |||
530 | static inline void | ||
531 | perf_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 | |||
1851 | struct 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 | |||
1867 | static 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 | |||
1883 | static 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 | |||
1897 | static 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 | |||
1913 | static 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 | |||
1938 | got_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(¤t->perf_counter_ctx, mmap_event); | ||
1951 | |||
1952 | kfree(buf); | ||
1953 | } | ||
1954 | |||
1955 | void 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 | |||
1973 | void 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 | ||
@@ -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); |
1225 | out: | 1226 | out: |
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); |