aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2011-05-03 13:25:24 -0400
committerSteven Rostedt <rostedt@goodmis.org>2011-05-18 15:29:47 -0400
commited926f9b35cda0988234c356e16a7cb30f4e5338 (patch)
tree32169b7aaf6b0ef7815b095a544cce93e884bb73 /kernel/trace
parent33dc9b1267d59cef46ff0bd6bc043190845dc919 (diff)
ftrace: Use counters to enable functions to trace
Every function has its own record that stores the instruction pointer and flags for the function to be traced. There are only two flags: enabled and free. The enabled flag states that tracing for the function has been enabled (actively traced), and the free flag states that the record no longer points to a function and can be used by new functions (loaded modules). These flags are now moved to the MSB of the flags (actually just the top 32bits). The rest of the bits (30 bits) are now used as a ref counter. Everytime a tracer register functions to trace, those functions will have its counter incremented. When tracing is enabled, to determine if a function should be traced, the counter is examined, and if it is non-zero it is set to trace. When a ftrace_ops is registered to trace functions, its hashes are examined. If the ftrace_ops filter_hash count is zero, then all functions are set to be traced, otherwise only the functions in the hash are to be traced. The exception to this is if a function is also in the ftrace_ops notrace_hash. Then that function's counter is not incremented for this ftrace_ops. Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r--kernel/trace/ftrace.c158
1 files changed, 142 insertions, 16 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 46f08264980b..5dd332cc5aa8 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -890,6 +890,10 @@ static const struct ftrace_hash empty_hash = {
890}; 890};
891#define EMPTY_HASH ((struct ftrace_hash *)&empty_hash) 891#define EMPTY_HASH ((struct ftrace_hash *)&empty_hash)
892 892
893enum {
894 FTRACE_OPS_FL_ENABLED = 1,
895};
896
893struct ftrace_ops global_ops = { 897struct ftrace_ops global_ops = {
894 .func = ftrace_stub, 898 .func = ftrace_stub,
895 .notrace_hash = EMPTY_HASH, 899 .notrace_hash = EMPTY_HASH,
@@ -1161,6 +1165,105 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
1161 } \ 1165 } \
1162 } 1166 }
1163 1167
1168static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
1169 int filter_hash,
1170 bool inc)
1171{
1172 struct ftrace_hash *hash;
1173 struct ftrace_hash *other_hash;
1174 struct ftrace_page *pg;
1175 struct dyn_ftrace *rec;
1176 int count = 0;
1177 int all = 0;
1178
1179 /* Only update if the ops has been registered */
1180 if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
1181 return;
1182
1183 /*
1184 * In the filter_hash case:
1185 * If the count is zero, we update all records.
1186 * Otherwise we just update the items in the hash.
1187 *
1188 * In the notrace_hash case:
1189 * We enable the update in the hash.
1190 * As disabling notrace means enabling the tracing,
1191 * and enabling notrace means disabling, the inc variable
1192 * gets inversed.
1193 */
1194 if (filter_hash) {
1195 hash = ops->filter_hash;
1196 other_hash = ops->notrace_hash;
1197 if (!hash->count)
1198 all = 1;
1199 } else {
1200 inc = !inc;
1201 hash = ops->notrace_hash;
1202 other_hash = ops->filter_hash;
1203 /*
1204 * If the notrace hash has no items,
1205 * then there's nothing to do.
1206 */
1207 if (!hash->count)
1208 return;
1209 }
1210
1211 do_for_each_ftrace_rec(pg, rec) {
1212 int in_other_hash = 0;
1213 int in_hash = 0;
1214 int match = 0;
1215
1216 if (all) {
1217 /*
1218 * Only the filter_hash affects all records.
1219 * Update if the record is not in the notrace hash.
1220 */
1221 if (!ftrace_lookup_ip(other_hash, rec->ip))
1222 match = 1;
1223 } else {
1224 in_hash = !!ftrace_lookup_ip(hash, rec->ip);
1225 in_other_hash = !!ftrace_lookup_ip(other_hash, rec->ip);
1226
1227 /*
1228 *
1229 */
1230 if (filter_hash && in_hash && !in_other_hash)
1231 match = 1;
1232 else if (!filter_hash && in_hash &&
1233 (in_other_hash || !other_hash->count))
1234 match = 1;
1235 }
1236 if (!match)
1237 continue;
1238
1239 if (inc) {
1240 rec->flags++;
1241 if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == FTRACE_REF_MAX))
1242 return;
1243 } else {
1244 if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == 0))
1245 return;
1246 rec->flags--;
1247 }
1248 count++;
1249 /* Shortcut, if we handled all records, we are done. */
1250 if (!all && count == hash->count)
1251 return;
1252 } while_for_each_ftrace_rec();
1253}
1254
1255static void ftrace_hash_rec_disable(struct ftrace_ops *ops,
1256 int filter_hash)
1257{
1258 __ftrace_hash_rec_update(ops, filter_hash, 0);
1259}
1260
1261static void ftrace_hash_rec_enable(struct ftrace_ops *ops,
1262 int filter_hash)
1263{
1264 __ftrace_hash_rec_update(ops, filter_hash, 1);
1265}
1266
1164static void ftrace_free_rec(struct dyn_ftrace *rec) 1267static void ftrace_free_rec(struct dyn_ftrace *rec)
1165{ 1268{
1166 rec->freelist = ftrace_free_records; 1269 rec->freelist = ftrace_free_records;
@@ -1276,26 +1379,24 @@ int ftrace_text_reserved(void *start, void *end)
1276static int 1379static int
1277__ftrace_replace_code(struct dyn_ftrace *rec, int enable) 1380__ftrace_replace_code(struct dyn_ftrace *rec, int enable)
1278{ 1381{
1279 struct ftrace_ops *ops = &global_ops;
1280 unsigned long ftrace_addr; 1382 unsigned long ftrace_addr;
1281 unsigned long flag = 0UL; 1383 unsigned long flag = 0UL;
1282 1384
1283 ftrace_addr = (unsigned long)FTRACE_ADDR; 1385 ftrace_addr = (unsigned long)FTRACE_ADDR;
1284 1386
1285 /* 1387 /*
1286 * If this record is not to be traced or we want to disable it, 1388 * If we are enabling tracing:
1287 * then disable it.
1288 * 1389 *
1289 * If we want to enable it and filtering is off, then enable it. 1390 * If the record has a ref count, then we need to enable it
1391 * because someone is using it.
1290 * 1392 *
1291 * If we want to enable it and filtering is on, enable it only if 1393 * Otherwise we make sure its disabled.
1292 * it's filtered 1394 *
1395 * If we are disabling tracing, then disable all records that
1396 * are enabled.
1293 */ 1397 */
1294 if (enable && !ftrace_lookup_ip(ops->notrace_hash, rec->ip)) { 1398 if (enable && (rec->flags & ~FTRACE_FL_MASK))
1295 if (!ops->filter_hash->count || 1399 flag = FTRACE_FL_ENABLED;
1296 ftrace_lookup_ip(ops->filter_hash, rec->ip))
1297 flag = FTRACE_FL_ENABLED;
1298 }
1299 1400
1300 /* If the state of this record hasn't changed, then do nothing */ 1401 /* If the state of this record hasn't changed, then do nothing */
1301 if ((rec->flags & FTRACE_FL_ENABLED) == flag) 1402 if ((rec->flags & FTRACE_FL_ENABLED) == flag)
@@ -1423,17 +1524,25 @@ static void ftrace_startup_enable(int command)
1423 1524
1424static void ftrace_startup(int command) 1525static void ftrace_startup(int command)
1425{ 1526{
1527 struct ftrace_ops *ops = &global_ops;
1528
1426 if (unlikely(ftrace_disabled)) 1529 if (unlikely(ftrace_disabled))
1427 return; 1530 return;
1428 1531
1429 ftrace_start_up++; 1532 ftrace_start_up++;
1430 command |= FTRACE_ENABLE_CALLS; 1533 command |= FTRACE_ENABLE_CALLS;
1431 1534
1535 ops->flags |= FTRACE_OPS_FL_ENABLED;
1536 if (ftrace_start_up == 1)
1537 ftrace_hash_rec_enable(ops, 1);
1538
1432 ftrace_startup_enable(command); 1539 ftrace_startup_enable(command);
1433} 1540}
1434 1541
1435static void ftrace_shutdown(int command) 1542static void ftrace_shutdown(int command)
1436{ 1543{
1544 struct ftrace_ops *ops = &global_ops;
1545
1437 if (unlikely(ftrace_disabled)) 1546 if (unlikely(ftrace_disabled))
1438 return; 1547 return;
1439 1548
@@ -1446,7 +1555,12 @@ static void ftrace_shutdown(int command)
1446 WARN_ON_ONCE(ftrace_start_up < 0); 1555 WARN_ON_ONCE(ftrace_start_up < 0);
1447 1556
1448 if (!ftrace_start_up) 1557 if (!ftrace_start_up)
1558 ftrace_hash_rec_disable(ops, 1);
1559
1560 if (!ftrace_start_up) {
1449 command |= FTRACE_DISABLE_CALLS; 1561 command |= FTRACE_DISABLE_CALLS;
1562 ops->flags &= ~FTRACE_OPS_FL_ENABLED;
1563 }
1450 1564
1451 if (saved_ftrace_func != ftrace_trace_function) { 1565 if (saved_ftrace_func != ftrace_trace_function) {
1452 saved_ftrace_func = ftrace_trace_function; 1566 saved_ftrace_func = ftrace_trace_function;
@@ -2668,6 +2782,7 @@ ftrace_regex_release(struct inode *inode, struct file *file)
2668 struct ftrace_iterator *iter; 2782 struct ftrace_iterator *iter;
2669 struct ftrace_hash **orig_hash; 2783 struct ftrace_hash **orig_hash;
2670 struct trace_parser *parser; 2784 struct trace_parser *parser;
2785 int filter_hash;
2671 int ret; 2786 int ret;
2672 2787
2673 mutex_lock(&ftrace_regex_lock); 2788 mutex_lock(&ftrace_regex_lock);
@@ -2687,15 +2802,26 @@ ftrace_regex_release(struct inode *inode, struct file *file)
2687 trace_parser_put(parser); 2802 trace_parser_put(parser);
2688 2803
2689 if (file->f_mode & FMODE_WRITE) { 2804 if (file->f_mode & FMODE_WRITE) {
2690 if (iter->flags & FTRACE_ITER_NOTRACE) 2805 filter_hash = !!(iter->flags & FTRACE_ITER_FILTER);
2691 orig_hash = &iter->ops->notrace_hash; 2806
2692 else 2807 if (filter_hash)
2693 orig_hash = &iter->ops->filter_hash; 2808 orig_hash = &iter->ops->filter_hash;
2809 else
2810 orig_hash = &iter->ops->notrace_hash;
2694 2811
2695 mutex_lock(&ftrace_lock); 2812 mutex_lock(&ftrace_lock);
2813 /*
2814 * Remove the current set, update the hash and add
2815 * them back.
2816 */
2817 ftrace_hash_rec_disable(iter->ops, filter_hash);
2696 ret = ftrace_hash_move(orig_hash, iter->hash); 2818 ret = ftrace_hash_move(orig_hash, iter->hash);
2697 if (!ret && ftrace_start_up && ftrace_enabled) 2819 if (!ret) {
2698 ftrace_run_update_code(FTRACE_ENABLE_CALLS); 2820 ftrace_hash_rec_enable(iter->ops, filter_hash);
2821 if (iter->ops->flags & FTRACE_OPS_FL_ENABLED
2822 && ftrace_enabled)
2823 ftrace_run_update_code(FTRACE_ENABLE_CALLS);
2824 }
2699 mutex_unlock(&ftrace_lock); 2825 mutex_unlock(&ftrace_lock);
2700 } 2826 }
2701 free_ftrace_hash(iter->hash); 2827 free_ftrace_hash(iter->hash);