aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2009-03-30 13:07:05 -0400
committerIngo Molnar <mingo@elte.hu>2009-04-06 03:30:38 -0400
commit0a4a93919bdc5cee48fe4367591e8e0449c1086c (patch)
tree0f0d7ac3ee4f61c1e7e53f0ba4e3b01e0e4ac728
parent195564390210977954fe4ef45b39cdee34f41b59 (diff)
perf_counter: executable mmap() information
Currently the profiling information returns userspace IPs but no way to correlate them to userspace code. Userspace could look into /proc/$pid/maps but that might not be current or even present anymore at the time of analyzing the IPs. Therefore provide means to track the mmap information and provide it in the output stream. XXX: only covers mmap()/munmap(), mremap() and mprotect() are missing. Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Acked-by: Paul Mackerras <paulus@samba.org> Cc: Andrew Morton <akpm@linux-foundation.org> Orig-LKML-Reference: <20090330171023.417259499@chello.nl> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-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);