diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
| -rw-r--r-- | kernel/trace/ftrace.c | 411 |
1 files changed, 258 insertions, 153 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 6dc4e5ef7a01..1e6640f80454 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -60,6 +60,13 @@ static int last_ftrace_enabled; | |||
| 60 | /* Quick disabling of function tracer. */ | 60 | /* Quick disabling of function tracer. */ |
| 61 | int function_trace_stop; | 61 | int function_trace_stop; |
| 62 | 62 | ||
| 63 | /* List for set_ftrace_pid's pids. */ | ||
| 64 | LIST_HEAD(ftrace_pids); | ||
| 65 | struct ftrace_pid { | ||
| 66 | struct list_head list; | ||
| 67 | struct pid *pid; | ||
| 68 | }; | ||
| 69 | |||
| 63 | /* | 70 | /* |
| 64 | * ftrace_disabled is set when an anomaly is discovered. | 71 | * ftrace_disabled is set when an anomaly is discovered. |
| 65 | * ftrace_disabled is much stronger than ftrace_enabled. | 72 | * ftrace_disabled is much stronger than ftrace_enabled. |
| @@ -78,6 +85,10 @@ ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; | |||
| 78 | ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; | 85 | ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; |
| 79 | ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; | 86 | ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; |
| 80 | 87 | ||
| 88 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
| 89 | static int ftrace_set_func(unsigned long *array, int *idx, char *buffer); | ||
| 90 | #endif | ||
| 91 | |||
| 81 | static void ftrace_list_func(unsigned long ip, unsigned long parent_ip) | 92 | static void ftrace_list_func(unsigned long ip, unsigned long parent_ip) |
| 82 | { | 93 | { |
| 83 | struct ftrace_ops *op = ftrace_list; | 94 | struct ftrace_ops *op = ftrace_list; |
| @@ -155,7 +166,7 @@ static int __register_ftrace_function(struct ftrace_ops *ops) | |||
| 155 | else | 166 | else |
| 156 | func = ftrace_list_func; | 167 | func = ftrace_list_func; |
| 157 | 168 | ||
| 158 | if (ftrace_pid_trace) { | 169 | if (!list_empty(&ftrace_pids)) { |
| 159 | set_ftrace_pid_function(func); | 170 | set_ftrace_pid_function(func); |
| 160 | func = ftrace_pid_func; | 171 | func = ftrace_pid_func; |
| 161 | } | 172 | } |
| @@ -203,7 +214,7 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) | |||
| 203 | if (ftrace_list->next == &ftrace_list_end) { | 214 | if (ftrace_list->next == &ftrace_list_end) { |
| 204 | ftrace_func_t func = ftrace_list->func; | 215 | ftrace_func_t func = ftrace_list->func; |
| 205 | 216 | ||
| 206 | if (ftrace_pid_trace) { | 217 | if (!list_empty(&ftrace_pids)) { |
| 207 | set_ftrace_pid_function(func); | 218 | set_ftrace_pid_function(func); |
| 208 | func = ftrace_pid_func; | 219 | func = ftrace_pid_func; |
| 209 | } | 220 | } |
| @@ -231,7 +242,7 @@ static void ftrace_update_pid_func(void) | |||
| 231 | func = __ftrace_trace_function; | 242 | func = __ftrace_trace_function; |
| 232 | #endif | 243 | #endif |
| 233 | 244 | ||
| 234 | if (ftrace_pid_trace) { | 245 | if (!list_empty(&ftrace_pids)) { |
| 235 | set_ftrace_pid_function(func); | 246 | set_ftrace_pid_function(func); |
| 236 | func = ftrace_pid_func; | 247 | func = ftrace_pid_func; |
| 237 | } else { | 248 | } else { |
| @@ -821,8 +832,6 @@ static __init void ftrace_profile_debugfs(struct dentry *d_tracer) | |||
| 821 | } | 832 | } |
| 822 | #endif /* CONFIG_FUNCTION_PROFILER */ | 833 | #endif /* CONFIG_FUNCTION_PROFILER */ |
| 823 | 834 | ||
| 824 | /* set when tracing only a pid */ | ||
| 825 | struct pid *ftrace_pid_trace; | ||
| 826 | static struct pid * const ftrace_swapper_pid = &init_struct_pid; | 835 | static struct pid * const ftrace_swapper_pid = &init_struct_pid; |
| 827 | 836 | ||
| 828 | #ifdef CONFIG_DYNAMIC_FTRACE | 837 | #ifdef CONFIG_DYNAMIC_FTRACE |
| @@ -1261,12 +1270,34 @@ static int ftrace_update_code(struct module *mod) | |||
| 1261 | ftrace_new_addrs = p->newlist; | 1270 | ftrace_new_addrs = p->newlist; |
| 1262 | p->flags = 0L; | 1271 | p->flags = 0L; |
| 1263 | 1272 | ||
| 1264 | /* convert record (i.e, patch mcount-call with NOP) */ | 1273 | /* |
| 1265 | if (ftrace_code_disable(mod, p)) { | 1274 | * Do the initial record convertion from mcount jump |
| 1266 | p->flags |= FTRACE_FL_CONVERTED; | 1275 | * to the NOP instructions. |
| 1267 | ftrace_update_cnt++; | 1276 | */ |
| 1268 | } else | 1277 | if (!ftrace_code_disable(mod, p)) { |
| 1269 | ftrace_free_rec(p); | 1278 | ftrace_free_rec(p); |
| 1279 | continue; | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | p->flags |= FTRACE_FL_CONVERTED; | ||
| 1283 | ftrace_update_cnt++; | ||
| 1284 | |||
| 1285 | /* | ||
| 1286 | * If the tracing is enabled, go ahead and enable the record. | ||
| 1287 | * | ||
| 1288 | * The reason not to enable the record immediatelly is the | ||
| 1289 | * inherent check of ftrace_make_nop/ftrace_make_call for | ||
| 1290 | * correct previous instructions. Making first the NOP | ||
| 1291 | * conversion puts the module to the correct state, thus | ||
| 1292 | * passing the ftrace_make_call check. | ||
| 1293 | */ | ||
| 1294 | if (ftrace_start_up) { | ||
| 1295 | int failed = __ftrace_replace_code(p, 1); | ||
| 1296 | if (failed) { | ||
| 1297 | ftrace_bug(failed, p->ip); | ||
| 1298 | ftrace_free_rec(p); | ||
| 1299 | } | ||
| 1300 | } | ||
| 1270 | } | 1301 | } |
| 1271 | 1302 | ||
| 1272 | stop = ftrace_now(raw_smp_processor_id()); | 1303 | stop = ftrace_now(raw_smp_processor_id()); |
| @@ -1656,64 +1687,10 @@ ftrace_regex_lseek(struct file *file, loff_t offset, int origin) | |||
| 1656 | return ret; | 1687 | return ret; |
| 1657 | } | 1688 | } |
| 1658 | 1689 | ||
| 1659 | enum { | ||
| 1660 | MATCH_FULL, | ||
| 1661 | MATCH_FRONT_ONLY, | ||
| 1662 | MATCH_MIDDLE_ONLY, | ||
| 1663 | MATCH_END_ONLY, | ||
| 1664 | }; | ||
| 1665 | |||
| 1666 | /* | ||
| 1667 | * (static function - no need for kernel doc) | ||
| 1668 | * | ||
| 1669 | * Pass in a buffer containing a glob and this function will | ||
| 1670 | * set search to point to the search part of the buffer and | ||
| 1671 | * return the type of search it is (see enum above). | ||
| 1672 | * This does modify buff. | ||
| 1673 | * | ||
| 1674 | * Returns enum type. | ||
| 1675 | * search returns the pointer to use for comparison. | ||
| 1676 | * not returns 1 if buff started with a '!' | ||
| 1677 | * 0 otherwise. | ||
| 1678 | */ | ||
| 1679 | static int | ||
| 1680 | ftrace_setup_glob(char *buff, int len, char **search, int *not) | ||
| 1681 | { | ||
| 1682 | int type = MATCH_FULL; | ||
| 1683 | int i; | ||
| 1684 | |||
| 1685 | if (buff[0] == '!') { | ||
| 1686 | *not = 1; | ||
| 1687 | buff++; | ||
| 1688 | len--; | ||
| 1689 | } else | ||
| 1690 | *not = 0; | ||
| 1691 | |||
| 1692 | *search = buff; | ||
| 1693 | |||
| 1694 | for (i = 0; i < len; i++) { | ||
| 1695 | if (buff[i] == '*') { | ||
| 1696 | if (!i) { | ||
| 1697 | *search = buff + 1; | ||
| 1698 | type = MATCH_END_ONLY; | ||
| 1699 | } else { | ||
| 1700 | if (type == MATCH_END_ONLY) | ||
| 1701 | type = MATCH_MIDDLE_ONLY; | ||
| 1702 | else | ||
| 1703 | type = MATCH_FRONT_ONLY; | ||
| 1704 | buff[i] = 0; | ||
| 1705 | break; | ||
| 1706 | } | ||
| 1707 | } | ||
| 1708 | } | ||
| 1709 | |||
| 1710 | return type; | ||
| 1711 | } | ||
| 1712 | |||
| 1713 | static int ftrace_match(char *str, char *regex, int len, int type) | 1690 | static int ftrace_match(char *str, char *regex, int len, int type) |
| 1714 | { | 1691 | { |
| 1715 | int matched = 0; | 1692 | int matched = 0; |
| 1716 | char *ptr; | 1693 | int slen; |
| 1717 | 1694 | ||
| 1718 | switch (type) { | 1695 | switch (type) { |
| 1719 | case MATCH_FULL: | 1696 | case MATCH_FULL: |
| @@ -1729,8 +1706,8 @@ static int ftrace_match(char *str, char *regex, int len, int type) | |||
| 1729 | matched = 1; | 1706 | matched = 1; |
| 1730 | break; | 1707 | break; |
| 1731 | case MATCH_END_ONLY: | 1708 | case MATCH_END_ONLY: |
| 1732 | ptr = strstr(str, regex); | 1709 | slen = strlen(str); |
| 1733 | if (ptr && (ptr[len] == 0)) | 1710 | if (slen >= len && memcmp(str + slen - len, regex, len) == 0) |
| 1734 | matched = 1; | 1711 | matched = 1; |
| 1735 | break; | 1712 | break; |
| 1736 | } | 1713 | } |
| @@ -1747,7 +1724,7 @@ ftrace_match_record(struct dyn_ftrace *rec, char *regex, int len, int type) | |||
| 1747 | return ftrace_match(str, regex, len, type); | 1724 | return ftrace_match(str, regex, len, type); |
| 1748 | } | 1725 | } |
| 1749 | 1726 | ||
| 1750 | static void ftrace_match_records(char *buff, int len, int enable) | 1727 | static int ftrace_match_records(char *buff, int len, int enable) |
| 1751 | { | 1728 | { |
| 1752 | unsigned int search_len; | 1729 | unsigned int search_len; |
| 1753 | struct ftrace_page *pg; | 1730 | struct ftrace_page *pg; |
| @@ -1756,9 +1733,10 @@ static void ftrace_match_records(char *buff, int len, int enable) | |||
| 1756 | char *search; | 1733 | char *search; |
| 1757 | int type; | 1734 | int type; |
| 1758 | int not; | 1735 | int not; |
| 1736 | int found = 0; | ||
| 1759 | 1737 | ||
| 1760 | flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 1738 | flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; |
| 1761 | type = ftrace_setup_glob(buff, len, &search, ¬); | 1739 | type = filter_parse_regex(buff, len, &search, ¬); |
| 1762 | 1740 | ||
| 1763 | search_len = strlen(search); | 1741 | search_len = strlen(search); |
| 1764 | 1742 | ||
| @@ -1773,6 +1751,7 @@ static void ftrace_match_records(char *buff, int len, int enable) | |||
| 1773 | rec->flags &= ~flag; | 1751 | rec->flags &= ~flag; |
| 1774 | else | 1752 | else |
| 1775 | rec->flags |= flag; | 1753 | rec->flags |= flag; |
| 1754 | found = 1; | ||
| 1776 | } | 1755 | } |
| 1777 | /* | 1756 | /* |
| 1778 | * Only enable filtering if we have a function that | 1757 | * Only enable filtering if we have a function that |
| @@ -1782,6 +1761,8 @@ static void ftrace_match_records(char *buff, int len, int enable) | |||
| 1782 | ftrace_filtered = 1; | 1761 | ftrace_filtered = 1; |
| 1783 | } while_for_each_ftrace_rec(); | 1762 | } while_for_each_ftrace_rec(); |
| 1784 | mutex_unlock(&ftrace_lock); | 1763 | mutex_unlock(&ftrace_lock); |
| 1764 | |||
| 1765 | return found; | ||
| 1785 | } | 1766 | } |
| 1786 | 1767 | ||
| 1787 | static int | 1768 | static int |
| @@ -1803,7 +1784,7 @@ ftrace_match_module_record(struct dyn_ftrace *rec, char *mod, | |||
| 1803 | return 1; | 1784 | return 1; |
| 1804 | } | 1785 | } |
| 1805 | 1786 | ||
| 1806 | static void ftrace_match_module_records(char *buff, char *mod, int enable) | 1787 | static int ftrace_match_module_records(char *buff, char *mod, int enable) |
| 1807 | { | 1788 | { |
| 1808 | unsigned search_len = 0; | 1789 | unsigned search_len = 0; |
| 1809 | struct ftrace_page *pg; | 1790 | struct ftrace_page *pg; |
| @@ -1812,6 +1793,7 @@ static void ftrace_match_module_records(char *buff, char *mod, int enable) | |||
| 1812 | char *search = buff; | 1793 | char *search = buff; |
| 1813 | unsigned long flag; | 1794 | unsigned long flag; |
| 1814 | int not = 0; | 1795 | int not = 0; |
| 1796 | int found = 0; | ||
| 1815 | 1797 | ||
| 1816 | flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 1798 | flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; |
| 1817 | 1799 | ||
| @@ -1826,7 +1808,7 @@ static void ftrace_match_module_records(char *buff, char *mod, int enable) | |||
| 1826 | } | 1808 | } |
| 1827 | 1809 | ||
| 1828 | if (strlen(buff)) { | 1810 | if (strlen(buff)) { |
| 1829 | type = ftrace_setup_glob(buff, strlen(buff), &search, ¬); | 1811 | type = filter_parse_regex(buff, strlen(buff), &search, ¬); |
| 1830 | search_len = strlen(search); | 1812 | search_len = strlen(search); |
| 1831 | } | 1813 | } |
| 1832 | 1814 | ||
| @@ -1842,12 +1824,15 @@ static void ftrace_match_module_records(char *buff, char *mod, int enable) | |||
| 1842 | rec->flags &= ~flag; | 1824 | rec->flags &= ~flag; |
| 1843 | else | 1825 | else |
| 1844 | rec->flags |= flag; | 1826 | rec->flags |= flag; |
| 1827 | found = 1; | ||
| 1845 | } | 1828 | } |
| 1846 | if (enable && (rec->flags & FTRACE_FL_FILTER)) | 1829 | if (enable && (rec->flags & FTRACE_FL_FILTER)) |
| 1847 | ftrace_filtered = 1; | 1830 | ftrace_filtered = 1; |
| 1848 | 1831 | ||
| 1849 | } while_for_each_ftrace_rec(); | 1832 | } while_for_each_ftrace_rec(); |
| 1850 | mutex_unlock(&ftrace_lock); | 1833 | mutex_unlock(&ftrace_lock); |
| 1834 | |||
| 1835 | return found; | ||
| 1851 | } | 1836 | } |
| 1852 | 1837 | ||
| 1853 | /* | 1838 | /* |
| @@ -1876,8 +1861,9 @@ ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | |||
| 1876 | if (!strlen(mod)) | 1861 | if (!strlen(mod)) |
| 1877 | return -EINVAL; | 1862 | return -EINVAL; |
| 1878 | 1863 | ||
| 1879 | ftrace_match_module_records(func, mod, enable); | 1864 | if (ftrace_match_module_records(func, mod, enable)) |
| 1880 | return 0; | 1865 | return 0; |
| 1866 | return -EINVAL; | ||
| 1881 | } | 1867 | } |
| 1882 | 1868 | ||
| 1883 | static struct ftrace_func_command ftrace_mod_cmd = { | 1869 | static struct ftrace_func_command ftrace_mod_cmd = { |
| @@ -1991,7 +1977,7 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, | |||
| 1991 | int count = 0; | 1977 | int count = 0; |
| 1992 | char *search; | 1978 | char *search; |
| 1993 | 1979 | ||
| 1994 | type = ftrace_setup_glob(glob, strlen(glob), &search, ¬); | 1980 | type = filter_parse_regex(glob, strlen(glob), &search, ¬); |
| 1995 | len = strlen(search); | 1981 | len = strlen(search); |
| 1996 | 1982 | ||
| 1997 | /* we do not support '!' for function probes */ | 1983 | /* we do not support '!' for function probes */ |
| @@ -2068,7 +2054,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, | |||
| 2068 | else if (glob) { | 2054 | else if (glob) { |
| 2069 | int not; | 2055 | int not; |
| 2070 | 2056 | ||
| 2071 | type = ftrace_setup_glob(glob, strlen(glob), &search, ¬); | 2057 | type = filter_parse_regex(glob, strlen(glob), &search, ¬); |
| 2072 | len = strlen(search); | 2058 | len = strlen(search); |
| 2073 | 2059 | ||
| 2074 | /* we do not support '!' for function probes */ | 2060 | /* we do not support '!' for function probes */ |
| @@ -2174,8 +2160,9 @@ static int ftrace_process_regex(char *buff, int len, int enable) | |||
| 2174 | func = strsep(&next, ":"); | 2160 | func = strsep(&next, ":"); |
| 2175 | 2161 | ||
| 2176 | if (!next) { | 2162 | if (!next) { |
| 2177 | ftrace_match_records(func, len, enable); | 2163 | if (ftrace_match_records(func, len, enable)) |
| 2178 | return 0; | 2164 | return 0; |
| 2165 | return ret; | ||
| 2179 | } | 2166 | } |
| 2180 | 2167 | ||
| 2181 | /* command found */ | 2168 | /* command found */ |
| @@ -2221,10 +2208,9 @@ ftrace_regex_write(struct file *file, const char __user *ubuf, | |||
| 2221 | !trace_parser_cont(parser)) { | 2208 | !trace_parser_cont(parser)) { |
| 2222 | ret = ftrace_process_regex(parser->buffer, | 2209 | ret = ftrace_process_regex(parser->buffer, |
| 2223 | parser->idx, enable); | 2210 | parser->idx, enable); |
| 2211 | trace_parser_clear(parser); | ||
| 2224 | if (ret) | 2212 | if (ret) |
| 2225 | goto out_unlock; | 2213 | goto out_unlock; |
| 2226 | |||
| 2227 | trace_parser_clear(parser); | ||
| 2228 | } | 2214 | } |
| 2229 | 2215 | ||
| 2230 | ret = read; | 2216 | ret = read; |
| @@ -2312,6 +2298,32 @@ static int __init set_ftrace_filter(char *str) | |||
| 2312 | } | 2298 | } |
| 2313 | __setup("ftrace_filter=", set_ftrace_filter); | 2299 | __setup("ftrace_filter=", set_ftrace_filter); |
| 2314 | 2300 | ||
| 2301 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
| 2302 | static char ftrace_graph_buf[FTRACE_FILTER_SIZE] __initdata; | ||
| 2303 | static int __init set_graph_function(char *str) | ||
| 2304 | { | ||
| 2305 | strlcpy(ftrace_graph_buf, str, FTRACE_FILTER_SIZE); | ||
| 2306 | return 1; | ||
| 2307 | } | ||
| 2308 | __setup("ftrace_graph_filter=", set_graph_function); | ||
| 2309 | |||
| 2310 | static void __init set_ftrace_early_graph(char *buf) | ||
| 2311 | { | ||
| 2312 | int ret; | ||
| 2313 | char *func; | ||
| 2314 | |||
| 2315 | while (buf) { | ||
| 2316 | func = strsep(&buf, ","); | ||
| 2317 | /* we allow only one expression at a time */ | ||
| 2318 | ret = ftrace_set_func(ftrace_graph_funcs, &ftrace_graph_count, | ||
| 2319 | func); | ||
| 2320 | if (ret) | ||
| 2321 | printk(KERN_DEBUG "ftrace: function %s not " | ||
| 2322 | "traceable\n", func); | ||
| 2323 | } | ||
| 2324 | } | ||
| 2325 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
| 2326 | |||
| 2315 | static void __init set_ftrace_early_filter(char *buf, int enable) | 2327 | static void __init set_ftrace_early_filter(char *buf, int enable) |
| 2316 | { | 2328 | { |
| 2317 | char *func; | 2329 | char *func; |
| @@ -2328,6 +2340,10 @@ static void __init set_ftrace_early_filters(void) | |||
| 2328 | set_ftrace_early_filter(ftrace_filter_buf, 1); | 2340 | set_ftrace_early_filter(ftrace_filter_buf, 1); |
| 2329 | if (ftrace_notrace_buf[0]) | 2341 | if (ftrace_notrace_buf[0]) |
| 2330 | set_ftrace_early_filter(ftrace_notrace_buf, 0); | 2342 | set_ftrace_early_filter(ftrace_notrace_buf, 0); |
| 2343 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
| 2344 | if (ftrace_graph_buf[0]) | ||
| 2345 | set_ftrace_early_graph(ftrace_graph_buf); | ||
| 2346 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
| 2331 | } | 2347 | } |
| 2332 | 2348 | ||
| 2333 | static int | 2349 | static int |
| @@ -2513,7 +2529,7 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer) | |||
| 2513 | return -ENODEV; | 2529 | return -ENODEV; |
| 2514 | 2530 | ||
| 2515 | /* decode regex */ | 2531 | /* decode regex */ |
| 2516 | type = ftrace_setup_glob(buffer, strlen(buffer), &search, ¬); | 2532 | type = filter_parse_regex(buffer, strlen(buffer), &search, ¬); |
| 2517 | if (not) | 2533 | if (not) |
| 2518 | return -EINVAL; | 2534 | return -EINVAL; |
| 2519 | 2535 | ||
| @@ -2536,10 +2552,9 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer) | |||
| 2536 | exists = true; | 2552 | exists = true; |
| 2537 | break; | 2553 | break; |
| 2538 | } | 2554 | } |
| 2539 | if (!exists) { | 2555 | if (!exists) |
| 2540 | array[(*idx)++] = rec->ip; | 2556 | array[(*idx)++] = rec->ip; |
| 2541 | found = 1; | 2557 | found = 1; |
| 2542 | } | ||
| 2543 | } | 2558 | } |
| 2544 | } while_for_each_ftrace_rec(); | 2559 | } while_for_each_ftrace_rec(); |
| 2545 | 2560 | ||
| @@ -2624,7 +2639,7 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer) | |||
| 2624 | return 0; | 2639 | return 0; |
| 2625 | } | 2640 | } |
| 2626 | 2641 | ||
| 2627 | static int ftrace_convert_nops(struct module *mod, | 2642 | static int ftrace_process_locs(struct module *mod, |
| 2628 | unsigned long *start, | 2643 | unsigned long *start, |
| 2629 | unsigned long *end) | 2644 | unsigned long *end) |
| 2630 | { | 2645 | { |
| @@ -2684,7 +2699,7 @@ static void ftrace_init_module(struct module *mod, | |||
| 2684 | { | 2699 | { |
| 2685 | if (ftrace_disabled || start == end) | 2700 | if (ftrace_disabled || start == end) |
| 2686 | return; | 2701 | return; |
| 2687 | ftrace_convert_nops(mod, start, end); | 2702 | ftrace_process_locs(mod, start, end); |
| 2688 | } | 2703 | } |
| 2689 | 2704 | ||
| 2690 | static int ftrace_module_notify(struct notifier_block *self, | 2705 | static int ftrace_module_notify(struct notifier_block *self, |
| @@ -2745,7 +2760,7 @@ void __init ftrace_init(void) | |||
| 2745 | 2760 | ||
| 2746 | last_ftrace_enabled = ftrace_enabled = 1; | 2761 | last_ftrace_enabled = ftrace_enabled = 1; |
| 2747 | 2762 | ||
| 2748 | ret = ftrace_convert_nops(NULL, | 2763 | ret = ftrace_process_locs(NULL, |
| 2749 | __start_mcount_loc, | 2764 | __start_mcount_loc, |
| 2750 | __stop_mcount_loc); | 2765 | __stop_mcount_loc); |
| 2751 | 2766 | ||
| @@ -2778,23 +2793,6 @@ static inline void ftrace_startup_enable(int command) { } | |||
| 2778 | # define ftrace_shutdown_sysctl() do { } while (0) | 2793 | # define ftrace_shutdown_sysctl() do { } while (0) |
| 2779 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 2794 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
| 2780 | 2795 | ||
| 2781 | static ssize_t | ||
| 2782 | ftrace_pid_read(struct file *file, char __user *ubuf, | ||
| 2783 | size_t cnt, loff_t *ppos) | ||
| 2784 | { | ||
| 2785 | char buf[64]; | ||
| 2786 | int r; | ||
| 2787 | |||
| 2788 | if (ftrace_pid_trace == ftrace_swapper_pid) | ||
| 2789 | r = sprintf(buf, "swapper tasks\n"); | ||
| 2790 | else if (ftrace_pid_trace) | ||
| 2791 | r = sprintf(buf, "%u\n", pid_vnr(ftrace_pid_trace)); | ||
| 2792 | else | ||
| 2793 | r = sprintf(buf, "no pid\n"); | ||
| 2794 | |||
| 2795 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); | ||
| 2796 | } | ||
| 2797 | |||
| 2798 | static void clear_ftrace_swapper(void) | 2796 | static void clear_ftrace_swapper(void) |
| 2799 | { | 2797 | { |
| 2800 | struct task_struct *p; | 2798 | struct task_struct *p; |
| @@ -2845,14 +2843,12 @@ static void set_ftrace_pid(struct pid *pid) | |||
| 2845 | rcu_read_unlock(); | 2843 | rcu_read_unlock(); |
| 2846 | } | 2844 | } |
| 2847 | 2845 | ||
| 2848 | static void clear_ftrace_pid_task(struct pid **pid) | 2846 | static void clear_ftrace_pid_task(struct pid *pid) |
| 2849 | { | 2847 | { |
| 2850 | if (*pid == ftrace_swapper_pid) | 2848 | if (pid == ftrace_swapper_pid) |
| 2851 | clear_ftrace_swapper(); | 2849 | clear_ftrace_swapper(); |
| 2852 | else | 2850 | else |
| 2853 | clear_ftrace_pid(*pid); | 2851 | clear_ftrace_pid(pid); |
| 2854 | |||
| 2855 | *pid = NULL; | ||
| 2856 | } | 2852 | } |
| 2857 | 2853 | ||
| 2858 | static void set_ftrace_pid_task(struct pid *pid) | 2854 | static void set_ftrace_pid_task(struct pid *pid) |
| @@ -2863,74 +2859,184 @@ static void set_ftrace_pid_task(struct pid *pid) | |||
| 2863 | set_ftrace_pid(pid); | 2859 | set_ftrace_pid(pid); |
| 2864 | } | 2860 | } |
| 2865 | 2861 | ||
| 2866 | static ssize_t | 2862 | static int ftrace_pid_add(int p) |
| 2867 | ftrace_pid_write(struct file *filp, const char __user *ubuf, | ||
| 2868 | size_t cnt, loff_t *ppos) | ||
| 2869 | { | 2863 | { |
| 2870 | struct pid *pid; | 2864 | struct pid *pid; |
| 2871 | char buf[64]; | 2865 | struct ftrace_pid *fpid; |
| 2872 | long val; | 2866 | int ret = -EINVAL; |
| 2873 | int ret; | ||
| 2874 | 2867 | ||
| 2875 | if (cnt >= sizeof(buf)) | 2868 | mutex_lock(&ftrace_lock); |
| 2876 | return -EINVAL; | ||
| 2877 | 2869 | ||
| 2878 | if (copy_from_user(&buf, ubuf, cnt)) | 2870 | if (!p) |
| 2879 | return -EFAULT; | 2871 | pid = ftrace_swapper_pid; |
| 2872 | else | ||
| 2873 | pid = find_get_pid(p); | ||
| 2880 | 2874 | ||
| 2881 | buf[cnt] = 0; | 2875 | if (!pid) |
| 2876 | goto out; | ||
| 2882 | 2877 | ||
| 2883 | ret = strict_strtol(buf, 10, &val); | 2878 | ret = 0; |
| 2884 | if (ret < 0) | ||
| 2885 | return ret; | ||
| 2886 | 2879 | ||
| 2887 | mutex_lock(&ftrace_lock); | 2880 | list_for_each_entry(fpid, &ftrace_pids, list) |
| 2888 | if (val < 0) { | 2881 | if (fpid->pid == pid) |
| 2889 | /* disable pid tracing */ | 2882 | goto out_put; |
| 2890 | if (!ftrace_pid_trace) | ||
| 2891 | goto out; | ||
| 2892 | 2883 | ||
| 2893 | clear_ftrace_pid_task(&ftrace_pid_trace); | 2884 | ret = -ENOMEM; |
| 2894 | 2885 | ||
| 2895 | } else { | 2886 | fpid = kmalloc(sizeof(*fpid), GFP_KERNEL); |
| 2896 | /* swapper task is special */ | 2887 | if (!fpid) |
| 2897 | if (!val) { | 2888 | goto out_put; |
| 2898 | pid = ftrace_swapper_pid; | ||
| 2899 | if (pid == ftrace_pid_trace) | ||
| 2900 | goto out; | ||
| 2901 | } else { | ||
| 2902 | pid = find_get_pid(val); | ||
| 2903 | 2889 | ||
| 2904 | if (pid == ftrace_pid_trace) { | 2890 | list_add(&fpid->list, &ftrace_pids); |
| 2905 | put_pid(pid); | 2891 | fpid->pid = pid; |
| 2906 | goto out; | ||
| 2907 | } | ||
| 2908 | } | ||
| 2909 | 2892 | ||
| 2910 | if (ftrace_pid_trace) | 2893 | set_ftrace_pid_task(pid); |
| 2911 | clear_ftrace_pid_task(&ftrace_pid_trace); | ||
| 2912 | 2894 | ||
| 2913 | if (!pid) | 2895 | ftrace_update_pid_func(); |
| 2914 | goto out; | 2896 | ftrace_startup_enable(0); |
| 2897 | |||
| 2898 | mutex_unlock(&ftrace_lock); | ||
| 2899 | return 0; | ||
| 2900 | |||
| 2901 | out_put: | ||
| 2902 | if (pid != ftrace_swapper_pid) | ||
| 2903 | put_pid(pid); | ||
| 2915 | 2904 | ||
| 2916 | ftrace_pid_trace = pid; | 2905 | out: |
| 2906 | mutex_unlock(&ftrace_lock); | ||
| 2907 | return ret; | ||
| 2908 | } | ||
| 2909 | |||
| 2910 | static void ftrace_pid_reset(void) | ||
| 2911 | { | ||
| 2912 | struct ftrace_pid *fpid, *safe; | ||
| 2917 | 2913 | ||
| 2918 | set_ftrace_pid_task(ftrace_pid_trace); | 2914 | mutex_lock(&ftrace_lock); |
| 2915 | list_for_each_entry_safe(fpid, safe, &ftrace_pids, list) { | ||
| 2916 | struct pid *pid = fpid->pid; | ||
| 2917 | |||
| 2918 | clear_ftrace_pid_task(pid); | ||
| 2919 | |||
| 2920 | list_del(&fpid->list); | ||
| 2921 | kfree(fpid); | ||
| 2919 | } | 2922 | } |
| 2920 | 2923 | ||
| 2921 | /* update the function call */ | ||
| 2922 | ftrace_update_pid_func(); | 2924 | ftrace_update_pid_func(); |
| 2923 | ftrace_startup_enable(0); | 2925 | ftrace_startup_enable(0); |
| 2924 | 2926 | ||
| 2925 | out: | ||
| 2926 | mutex_unlock(&ftrace_lock); | 2927 | mutex_unlock(&ftrace_lock); |
| 2928 | } | ||
| 2927 | 2929 | ||
| 2928 | return cnt; | 2930 | static void *fpid_start(struct seq_file *m, loff_t *pos) |
| 2931 | { | ||
| 2932 | mutex_lock(&ftrace_lock); | ||
| 2933 | |||
| 2934 | if (list_empty(&ftrace_pids) && (!*pos)) | ||
| 2935 | return (void *) 1; | ||
| 2936 | |||
| 2937 | return seq_list_start(&ftrace_pids, *pos); | ||
| 2938 | } | ||
| 2939 | |||
| 2940 | static void *fpid_next(struct seq_file *m, void *v, loff_t *pos) | ||
| 2941 | { | ||
| 2942 | if (v == (void *)1) | ||
| 2943 | return NULL; | ||
| 2944 | |||
| 2945 | return seq_list_next(v, &ftrace_pids, pos); | ||
| 2946 | } | ||
| 2947 | |||
| 2948 | static void fpid_stop(struct seq_file *m, void *p) | ||
| 2949 | { | ||
| 2950 | mutex_unlock(&ftrace_lock); | ||
| 2951 | } | ||
| 2952 | |||
| 2953 | static int fpid_show(struct seq_file *m, void *v) | ||
| 2954 | { | ||
| 2955 | const struct ftrace_pid *fpid = list_entry(v, struct ftrace_pid, list); | ||
| 2956 | |||
| 2957 | if (v == (void *)1) { | ||
| 2958 | seq_printf(m, "no pid\n"); | ||
| 2959 | return 0; | ||
| 2960 | } | ||
| 2961 | |||
| 2962 | if (fpid->pid == ftrace_swapper_pid) | ||
| 2963 | seq_printf(m, "swapper tasks\n"); | ||
| 2964 | else | ||
| 2965 | seq_printf(m, "%u\n", pid_vnr(fpid->pid)); | ||
| 2966 | |||
| 2967 | return 0; | ||
| 2968 | } | ||
| 2969 | |||
| 2970 | static const struct seq_operations ftrace_pid_sops = { | ||
| 2971 | .start = fpid_start, | ||
| 2972 | .next = fpid_next, | ||
| 2973 | .stop = fpid_stop, | ||
| 2974 | .show = fpid_show, | ||
| 2975 | }; | ||
| 2976 | |||
| 2977 | static int | ||
| 2978 | ftrace_pid_open(struct inode *inode, struct file *file) | ||
| 2979 | { | ||
| 2980 | int ret = 0; | ||
| 2981 | |||
| 2982 | if ((file->f_mode & FMODE_WRITE) && | ||
| 2983 | (file->f_flags & O_TRUNC)) | ||
| 2984 | ftrace_pid_reset(); | ||
| 2985 | |||
| 2986 | if (file->f_mode & FMODE_READ) | ||
| 2987 | ret = seq_open(file, &ftrace_pid_sops); | ||
| 2988 | |||
| 2989 | return ret; | ||
| 2990 | } | ||
| 2991 | |||
| 2992 | static ssize_t | ||
| 2993 | ftrace_pid_write(struct file *filp, const char __user *ubuf, | ||
| 2994 | size_t cnt, loff_t *ppos) | ||
| 2995 | { | ||
| 2996 | char buf[64], *tmp; | ||
| 2997 | long val; | ||
| 2998 | int ret; | ||
| 2999 | |||
| 3000 | if (cnt >= sizeof(buf)) | ||
| 3001 | return -EINVAL; | ||
| 3002 | |||
| 3003 | if (copy_from_user(&buf, ubuf, cnt)) | ||
| 3004 | return -EFAULT; | ||
| 3005 | |||
| 3006 | buf[cnt] = 0; | ||
| 3007 | |||
| 3008 | /* | ||
| 3009 | * Allow "echo > set_ftrace_pid" or "echo -n '' > set_ftrace_pid" | ||
| 3010 | * to clean the filter quietly. | ||
| 3011 | */ | ||
| 3012 | tmp = strstrip(buf); | ||
| 3013 | if (strlen(tmp) == 0) | ||
| 3014 | return 1; | ||
| 3015 | |||
| 3016 | ret = strict_strtol(tmp, 10, &val); | ||
| 3017 | if (ret < 0) | ||
| 3018 | return ret; | ||
| 3019 | |||
| 3020 | ret = ftrace_pid_add(val); | ||
| 3021 | |||
| 3022 | return ret ? ret : cnt; | ||
| 3023 | } | ||
| 3024 | |||
| 3025 | static int | ||
| 3026 | ftrace_pid_release(struct inode *inode, struct file *file) | ||
| 3027 | { | ||
| 3028 | if (file->f_mode & FMODE_READ) | ||
| 3029 | seq_release(inode, file); | ||
| 3030 | |||
| 3031 | return 0; | ||
| 2929 | } | 3032 | } |
| 2930 | 3033 | ||
| 2931 | static const struct file_operations ftrace_pid_fops = { | 3034 | static const struct file_operations ftrace_pid_fops = { |
| 2932 | .read = ftrace_pid_read, | 3035 | .open = ftrace_pid_open, |
| 2933 | .write = ftrace_pid_write, | 3036 | .write = ftrace_pid_write, |
| 3037 | .read = seq_read, | ||
| 3038 | .llseek = seq_lseek, | ||
| 3039 | .release = ftrace_pid_release, | ||
| 2934 | }; | 3040 | }; |
| 2935 | 3041 | ||
| 2936 | static __init int ftrace_init_debugfs(void) | 3042 | static __init int ftrace_init_debugfs(void) |
| @@ -3293,4 +3399,3 @@ void ftrace_graph_stop(void) | |||
| 3293 | ftrace_stop(); | 3399 | ftrace_stop(); |
| 3294 | } | 3400 | } |
| 3295 | #endif | 3401 | #endif |
| 3296 | |||
