Currently Function traces can be only exported to the ring buffer. This adds a trace_export concept which can process traces and export them to a registered destination as an addition to the current one that outputs to Ftrace - i.e. ring buffer. In this way, if we want function traces to be sent to other destinations rather than only to the ring buffer, we just need to register a new trace_export and implement its own .write() function for writing traces to storage. With this patch, only function tracing (trace type is TRACE_FN) is supported. Link: http://lkml.kernel.org/r/1479715043-6534-2-git-send-email-zhang.chunyan@linaro.org
Unified · Split
| @@ -0,0 +1,28 @@ | ||
| 1 | +#ifndef _LINUX_TRACE_H | |
| 2 | +#define _LINUX_TRACE_H | |
| 3 | + | |
| 4 | +#ifdef CONFIG_TRACING | |
| 5 | +/* | |
| 6 | + * The trace export - an export of Ftrace output. The trace_export | |
| 7 | + * can process traces and export them to a registered destination as | |
| 8 | + * an addition to the current only output of Ftrace - i.e. ring buffer. | |
| 9 | + * | |
| 10 | + * If you want traces to be sent to some other place rather than ring | |
| 11 | + * buffer only, just need to register a new trace_export and implement | |
| 12 | + * its own .write() function for writing traces to the storage. | |
| 13 | + * | |
| 14 | + * next - pointer to the next trace_export | |
| 15 | + * write - copy traces which have been delt with ->commit() to | |
| 16 | + * the destination | |
| 17 | + */ | |
| 18 | +struct trace_export { | |
| 19 | + struct trace_export __rcu *next; | |
| 20 | + void (*write)(const void *, unsigned int); | |
| 21 | +}; | |
| 22 | + | |
| 23 | +int register_ftrace_export(struct trace_export *export); | |
| 24 | +int unregister_ftrace_export(struct trace_export *export); | |
| 25 | + | |
| 26 | +#endif /* CONFIG_TRACING */ | |
| 27 | + | |
| 28 | +#endif /* _LINUX_TRACE_H */ | |
| @@ -40,6 +40,7 @@ · 39 unmodified lines | ||
| 40 | 40 | #include <linux/poll.h> |
| 41 | 41 | #include <linux/nmi.h> |
| 42 | 42 | #include <linux/fs.h> |
| 43 | +#include <linux/trace.h> | |
| 43 | 44 | #include <linux/sched/rt.h> |
| 44 | 45 | |
| 45 | 46 | #include "trace.h" |
| @@ -2128,6 +2129,129 @@ void trace_buffer_unlock_commit_regs(struct trace_array *tr, · 2082 unmodified lines | ||
| 2128 | 2129 | ftrace_trace_userstack(buffer, flags, pc); |
| 2129 | 2130 | } |
| 2130 | 2131 | |
| 2132 | +static void | |
| 2133 | +trace_process_export(struct trace_export *export, | |
| 2134 | + struct ring_buffer_event *event) | |
| 2135 | +{ | |
| 2136 | + struct trace_entry *entry; | |
| 2137 | + unsigned int size = 0; | |
| 2138 | + | |
| 2139 | + entry = ring_buffer_event_data(event); | |
| 2140 | + size = ring_buffer_event_length(event); | |
| 2141 | + export->write(entry, size); | |
| 2142 | +} | |
| 2143 | + | |
| 2144 | +static DEFINE_MUTEX(ftrace_export_lock); | |
| 2145 | + | |
| 2146 | +static struct trace_export __rcu *ftrace_exports_list __read_mostly; | |
| 2147 | + | |
| 2148 | +static DEFINE_STATIC_KEY_FALSE(ftrace_exports_enabled); | |
| 2149 | + | |
| 2150 | +static inline void ftrace_exports_enable(void) | |
| 2151 | +{ | |
| 2152 | + static_branch_enable(&ftrace_exports_enabled); | |
| 2153 | +} | |
| 2154 | + | |
| 2155 | +static inline void ftrace_exports_disable(void) | |
| 2156 | +{ | |
| 2157 | + static_branch_disable(&ftrace_exports_enabled); | |
| 2158 | +} | |
| 2159 | + | |
| 2160 | +void ftrace_exports(struct ring_buffer_event *event) | |
| 2161 | +{ | |
| 2162 | + struct trace_export *export; | |
| 2163 | + | |
| 2164 | + preempt_disable_notrace(); | |
| 2165 | + | |
| 2166 | + export = rcu_dereference_raw_notrace(ftrace_exports_list); | |
| 2167 | + while (export) { | |
| 2168 | + trace_process_export(export, event); | |
| 2169 | + export = rcu_dereference_raw_notrace(export->next); | |
| 2170 | + } | |
| 2171 | + | |
| 2172 | + preempt_enable_notrace(); | |
| 2173 | +} | |
| 2174 | + | |
| 2175 | +static inline void | |
| 2176 | +add_trace_export(struct trace_export **list, struct trace_export *export) | |
| 2177 | +{ | |
| 2178 | + rcu_assign_pointer(export->next, *list); | |
| 2179 | + /* | |
| 2180 | + * We are entering export into the list but another | |
| 2181 | + * CPU might be walking that list. We need to make sure | |
| 2182 | + * the export->next pointer is valid before another CPU sees | |
| 2183 | + * the export pointer included into the list. | |
| 2184 | + */ | |
| 2185 | + rcu_assign_pointer(*list, export); | |
| 2186 | +} | |
| 2187 | + | |
| 2188 | +static inline int | |
| 2189 | +rm_trace_export(struct trace_export **list, struct trace_export *export) | |
| 2190 | +{ | |
| 2191 | + struct trace_export **p; | |
| 2192 | + | |
| 2193 | + for (p = list; *p != NULL; p = &(*p)->next) | |
| 2194 | + if (*p == export) | |
| 2195 | + break; | |
| 2196 | + | |
| 2197 | + if (*p != export) | |
| 2198 | + return -1; | |
| 2199 | + | |
| 2200 | + rcu_assign_pointer(*p, (*p)->next); | |
| 2201 | + | |
| 2202 | + return 0; | |
| 2203 | +} | |
| 2204 | + | |
| 2205 | +static inline void | |
| 2206 | +add_ftrace_export(struct trace_export **list, struct trace_export *export) | |
| 2207 | +{ | |
| 2208 | + if (*list == NULL) | |
| 2209 | + ftrace_exports_enable(); | |
| 2210 | + | |
| 2211 | + add_trace_export(list, export); | |
| 2212 | +} | |
| 2213 | + | |
| 2214 | +static inline int | |
| 2215 | +rm_ftrace_export(struct trace_export **list, struct trace_export *export) | |
| 2216 | +{ | |
| 2217 | + int ret; | |
| 2218 | + | |
| 2219 | + ret = rm_trace_export(list, export); | |
| 2220 | + if (*list == NULL) | |
| 2221 | + ftrace_exports_disable(); | |
| 2222 | + | |
| 2223 | + return ret; | |
| 2224 | +} | |
| 2225 | + | |
| 2226 | +int register_ftrace_export(struct trace_export *export) | |
| 2227 | +{ | |
| 2228 | + if (WARN_ON_ONCE(!export->write)) | |
| 2229 | + return -1; | |
| 2230 | + | |
| 2231 | + mutex_lock(&ftrace_export_lock); | |
| 2232 | + | |
| 2233 | + add_ftrace_export(&ftrace_exports_list, export); | |
| 2234 | + | |
| 2235 | + mutex_unlock(&ftrace_export_lock); | |
| 2236 | + | |
| 2237 | + return 0; | |
| 2238 | +} | |
| 2239 | +EXPORT_SYMBOL_GPL(register_ftrace_export); | |
| 2240 | + | |
| 2241 | +int unregister_ftrace_export(struct trace_export *export) | |
| 2242 | +{ | |
| 2243 | + int ret; | |
| 2244 | + | |
| 2245 | + mutex_lock(&ftrace_export_lock); | |
| 2246 | + | |
| 2247 | + ret = rm_ftrace_export(&ftrace_exports_list, export); | |
| 2248 | + | |
| 2249 | + mutex_unlock(&ftrace_export_lock); | |
| 2250 | + | |
| 2251 | + return ret; | |
| 2252 | +} | |
| 2253 | +EXPORT_SYMBOL_GPL(unregister_ftrace_export); | |
| 2254 | + | |
| 2131 | 2255 | void |
| 2132 | 2256 | trace_function(struct trace_array *tr, |
| 2133 | 2257 | unsigned long ip, unsigned long parent_ip, unsigned long flags, |
| @@ -2146,8 +2270,11 @@ trace_function(struct trace_array *tr, · 12 unmodified lines | ||
| 2146 | 2270 | entry->ip = ip; |
| 2147 | 2271 | entry->parent_ip = parent_ip; |
| 2148 | 2272 | |
| 2149 | − if (!call_filter_check_discard(call, entry, buffer, event)) | |
| 2273 | + if (!call_filter_check_discard(call, entry, buffer, event)) { | |
| 2274 | + if (static_branch_unlikely(&ftrace_exports_enabled)) | |
| 2275 | + ftrace_exports(event); | |
| 2150 | 2276 | __buffer_unlock_commit(buffer, event); |
| 2277 | + } | |
| 2151 | 2278 | } |
| 2152 | 2279 | |
| 2153 | 2280 | #ifdef CONFIG_STACKTRACE |