diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/perf_counter.c | 145 |
1 files changed, 145 insertions, 0 deletions
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 | ||