diff options
| -rw-r--r-- | include/linux/ftrace.h | 46 | ||||
| -rw-r--r-- | include/linux/sched.h | 4 | ||||
| -rw-r--r-- | kernel/trace/ftrace.c | 227 | ||||
| -rw-r--r-- | kernel/trace/trace.c | 8 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 30 |
5 files changed, 314 insertions, 1 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 469ceb3e85ba..b295d3106bfe 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <linux/init.h> | 7 | #include <linux/init.h> |
| 8 | #include <linux/types.h> | 8 | #include <linux/types.h> |
| 9 | #include <linux/kallsyms.h> | 9 | #include <linux/kallsyms.h> |
| 10 | #include <linux/bitops.h> | ||
| 10 | 11 | ||
| 11 | #ifdef CONFIG_FUNCTION_TRACER | 12 | #ifdef CONFIG_FUNCTION_TRACER |
| 12 | 13 | ||
| @@ -391,4 +392,49 @@ static inline void ftrace_graph_init_task(struct task_struct *t) { } | |||
| 391 | static inline void ftrace_graph_exit_task(struct task_struct *t) { } | 392 | static inline void ftrace_graph_exit_task(struct task_struct *t) { } |
| 392 | #endif | 393 | #endif |
| 393 | 394 | ||
| 395 | #ifdef CONFIG_TRACING | ||
| 396 | #include <linux/sched.h> | ||
| 397 | |||
| 398 | /* flags for current->trace */ | ||
| 399 | enum { | ||
| 400 | TSK_TRACE_FL_TRACE_BIT = 0, | ||
| 401 | TSK_TRACE_FL_GRAPH_BIT = 1, | ||
| 402 | }; | ||
| 403 | enum { | ||
| 404 | TSK_TRACE_FL_TRACE = 1 << TSK_TRACE_FL_TRACE_BIT, | ||
| 405 | TSK_TRACE_FL_GRAPH = 1 << TSK_TRACE_FL_GRAPH_BIT, | ||
| 406 | }; | ||
| 407 | |||
| 408 | static inline void set_tsk_trace_trace(struct task_struct *tsk) | ||
| 409 | { | ||
| 410 | set_bit(TSK_TRACE_FL_TRACE_BIT, &tsk->trace); | ||
| 411 | } | ||
| 412 | |||
| 413 | static inline void clear_tsk_trace_trace(struct task_struct *tsk) | ||
| 414 | { | ||
| 415 | clear_bit(TSK_TRACE_FL_TRACE_BIT, &tsk->trace); | ||
| 416 | } | ||
| 417 | |||
| 418 | static inline int test_tsk_trace_trace(struct task_struct *tsk) | ||
| 419 | { | ||
| 420 | return tsk->trace & TSK_TRACE_FL_TRACE; | ||
| 421 | } | ||
| 422 | |||
| 423 | static inline void set_tsk_trace_graph(struct task_struct *tsk) | ||
| 424 | { | ||
| 425 | set_bit(TSK_TRACE_FL_GRAPH_BIT, &tsk->trace); | ||
| 426 | } | ||
| 427 | |||
| 428 | static inline void clear_tsk_trace_graph(struct task_struct *tsk) | ||
| 429 | { | ||
| 430 | clear_bit(TSK_TRACE_FL_GRAPH_BIT, &tsk->trace); | ||
| 431 | } | ||
| 432 | |||
| 433 | static inline int test_tsk_trace_graph(struct task_struct *tsk) | ||
| 434 | { | ||
| 435 | return tsk->trace & TSK_TRACE_FL_GRAPH; | ||
| 436 | } | ||
| 437 | |||
| 438 | #endif /* CONFIG_TRACING */ | ||
| 439 | |||
| 394 | #endif /* _LINUX_FTRACE_H */ | 440 | #endif /* _LINUX_FTRACE_H */ |
diff --git a/include/linux/sched.h b/include/linux/sched.h index 2d0a93c31228..4c152e0acc9e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
| @@ -1380,6 +1380,10 @@ struct task_struct { | |||
| 1380 | */ | 1380 | */ |
| 1381 | atomic_t trace_overrun; | 1381 | atomic_t trace_overrun; |
| 1382 | #endif | 1382 | #endif |
| 1383 | #ifdef CONFIG_TRACING | ||
| 1384 | /* state flags for use by tracers */ | ||
| 1385 | unsigned long trace; | ||
| 1386 | #endif | ||
| 1383 | }; | 1387 | }; |
| 1384 | 1388 | ||
| 1385 | /* | 1389 | /* |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 65b9e863056b..b17a30350f06 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -1320,6 +1320,224 @@ static struct file_operations ftrace_notrace_fops = { | |||
| 1320 | .release = ftrace_notrace_release, | 1320 | .release = ftrace_notrace_release, |
| 1321 | }; | 1321 | }; |
| 1322 | 1322 | ||
| 1323 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
| 1324 | |||
| 1325 | static DEFINE_MUTEX(graph_lock); | ||
| 1326 | |||
| 1327 | int ftrace_graph_count; | ||
| 1328 | unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; | ||
| 1329 | |||
| 1330 | static void * | ||
| 1331 | g_next(struct seq_file *m, void *v, loff_t *pos) | ||
| 1332 | { | ||
| 1333 | unsigned long *array = m->private; | ||
| 1334 | int index = *pos; | ||
| 1335 | |||
| 1336 | (*pos)++; | ||
| 1337 | |||
| 1338 | if (index >= ftrace_graph_count) | ||
| 1339 | return NULL; | ||
| 1340 | |||
| 1341 | return &array[index]; | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | static void *g_start(struct seq_file *m, loff_t *pos) | ||
| 1345 | { | ||
| 1346 | void *p = NULL; | ||
| 1347 | |||
| 1348 | mutex_lock(&graph_lock); | ||
| 1349 | |||
| 1350 | p = g_next(m, p, pos); | ||
| 1351 | |||
| 1352 | return p; | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | static void g_stop(struct seq_file *m, void *p) | ||
| 1356 | { | ||
| 1357 | mutex_unlock(&graph_lock); | ||
| 1358 | } | ||
| 1359 | |||
| 1360 | static int g_show(struct seq_file *m, void *v) | ||
| 1361 | { | ||
| 1362 | unsigned long *ptr = v; | ||
| 1363 | char str[KSYM_SYMBOL_LEN]; | ||
| 1364 | |||
| 1365 | if (!ptr) | ||
| 1366 | return 0; | ||
| 1367 | |||
| 1368 | kallsyms_lookup(*ptr, NULL, NULL, NULL, str); | ||
| 1369 | |||
| 1370 | seq_printf(m, "%s\n", str); | ||
| 1371 | |||
| 1372 | return 0; | ||
| 1373 | } | ||
| 1374 | |||
| 1375 | static struct seq_operations ftrace_graph_seq_ops = { | ||
| 1376 | .start = g_start, | ||
| 1377 | .next = g_next, | ||
| 1378 | .stop = g_stop, | ||
| 1379 | .show = g_show, | ||
| 1380 | }; | ||
| 1381 | |||
| 1382 | static int | ||
| 1383 | ftrace_graph_open(struct inode *inode, struct file *file) | ||
| 1384 | { | ||
| 1385 | int ret = 0; | ||
| 1386 | |||
| 1387 | if (unlikely(ftrace_disabled)) | ||
| 1388 | return -ENODEV; | ||
| 1389 | |||
| 1390 | mutex_lock(&graph_lock); | ||
| 1391 | if ((file->f_mode & FMODE_WRITE) && | ||
| 1392 | !(file->f_flags & O_APPEND)) { | ||
| 1393 | ftrace_graph_count = 0; | ||
| 1394 | memset(ftrace_graph_funcs, 0, sizeof(ftrace_graph_funcs)); | ||
| 1395 | } | ||
| 1396 | |||
| 1397 | if (file->f_mode & FMODE_READ) { | ||
| 1398 | ret = seq_open(file, &ftrace_graph_seq_ops); | ||
| 1399 | if (!ret) { | ||
| 1400 | struct seq_file *m = file->private_data; | ||
| 1401 | m->private = ftrace_graph_funcs; | ||
| 1402 | } | ||
| 1403 | } else | ||
| 1404 | file->private_data = ftrace_graph_funcs; | ||
| 1405 | mutex_unlock(&graph_lock); | ||
| 1406 | |||
| 1407 | return ret; | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | static ssize_t | ||
| 1411 | ftrace_graph_read(struct file *file, char __user *ubuf, | ||
| 1412 | size_t cnt, loff_t *ppos) | ||
| 1413 | { | ||
| 1414 | if (file->f_mode & FMODE_READ) | ||
| 1415 | return seq_read(file, ubuf, cnt, ppos); | ||
| 1416 | else | ||
| 1417 | return -EPERM; | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | static int | ||
| 1421 | ftrace_set_func(unsigned long *array, int idx, char *buffer) | ||
| 1422 | { | ||
| 1423 | char str[KSYM_SYMBOL_LEN]; | ||
| 1424 | struct dyn_ftrace *rec; | ||
| 1425 | struct ftrace_page *pg; | ||
| 1426 | int found = 0; | ||
| 1427 | int i; | ||
| 1428 | |||
| 1429 | if (ftrace_disabled) | ||
| 1430 | return -ENODEV; | ||
| 1431 | |||
| 1432 | /* should not be called from interrupt context */ | ||
| 1433 | spin_lock(&ftrace_lock); | ||
| 1434 | |||
| 1435 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | ||
| 1436 | for (i = 0; i < pg->index; i++) { | ||
| 1437 | rec = &pg->records[i]; | ||
| 1438 | |||
| 1439 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) | ||
| 1440 | continue; | ||
| 1441 | |||
| 1442 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | ||
| 1443 | if (strcmp(str, buffer) == 0) { | ||
| 1444 | found = 1; | ||
| 1445 | array[idx] = rec->ip; | ||
| 1446 | break; | ||
| 1447 | } | ||
| 1448 | } | ||
| 1449 | } | ||
| 1450 | spin_unlock(&ftrace_lock); | ||
| 1451 | |||
| 1452 | return found ? 0 : -EINVAL; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | static ssize_t | ||
| 1456 | ftrace_graph_write(struct file *file, const char __user *ubuf, | ||
| 1457 | size_t cnt, loff_t *ppos) | ||
| 1458 | { | ||
| 1459 | unsigned char buffer[FTRACE_BUFF_MAX+1]; | ||
| 1460 | unsigned long *array; | ||
| 1461 | size_t read = 0; | ||
| 1462 | ssize_t ret; | ||
| 1463 | int index = 0; | ||
| 1464 | char ch; | ||
| 1465 | |||
| 1466 | if (!cnt || cnt < 0) | ||
| 1467 | return 0; | ||
| 1468 | |||
| 1469 | mutex_lock(&graph_lock); | ||
| 1470 | |||
| 1471 | if (ftrace_graph_count >= FTRACE_GRAPH_MAX_FUNCS) { | ||
| 1472 | ret = -EBUSY; | ||
| 1473 | goto out; | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | if (file->f_mode & FMODE_READ) { | ||
| 1477 | struct seq_file *m = file->private_data; | ||
| 1478 | array = m->private; | ||
| 1479 | } else | ||
| 1480 | array = file->private_data; | ||
| 1481 | |||
| 1482 | ret = get_user(ch, ubuf++); | ||
| 1483 | if (ret) | ||
| 1484 | goto out; | ||
| 1485 | read++; | ||
| 1486 | cnt--; | ||
| 1487 | |||
| 1488 | /* skip white space */ | ||
| 1489 | while (cnt && isspace(ch)) { | ||
| 1490 | ret = get_user(ch, ubuf++); | ||
| 1491 | if (ret) | ||
| 1492 | goto out; | ||
| 1493 | read++; | ||
| 1494 | cnt--; | ||
| 1495 | } | ||
| 1496 | |||
| 1497 | if (isspace(ch)) { | ||
| 1498 | *ppos += read; | ||
| 1499 | ret = read; | ||
| 1500 | goto out; | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | while (cnt && !isspace(ch)) { | ||
| 1504 | if (index < FTRACE_BUFF_MAX) | ||
| 1505 | buffer[index++] = ch; | ||
| 1506 | else { | ||
| 1507 | ret = -EINVAL; | ||
| 1508 | goto out; | ||
| 1509 | } | ||
| 1510 | ret = get_user(ch, ubuf++); | ||
| 1511 | if (ret) | ||
| 1512 | goto out; | ||
| 1513 | read++; | ||
| 1514 | cnt--; | ||
| 1515 | } | ||
| 1516 | buffer[index] = 0; | ||
| 1517 | |||
| 1518 | /* we allow only one at a time */ | ||
| 1519 | ret = ftrace_set_func(array, ftrace_graph_count, buffer); | ||
| 1520 | if (ret) | ||
| 1521 | goto out; | ||
| 1522 | |||
| 1523 | ftrace_graph_count++; | ||
| 1524 | |||
| 1525 | file->f_pos += read; | ||
| 1526 | |||
| 1527 | ret = read; | ||
| 1528 | out: | ||
| 1529 | mutex_unlock(&graph_lock); | ||
| 1530 | |||
| 1531 | return ret; | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | static const struct file_operations ftrace_graph_fops = { | ||
| 1535 | .open = ftrace_graph_open, | ||
| 1536 | .read = ftrace_graph_read, | ||
| 1537 | .write = ftrace_graph_write, | ||
| 1538 | }; | ||
| 1539 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
| 1540 | |||
| 1323 | static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer) | 1541 | static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer) |
| 1324 | { | 1542 | { |
| 1325 | struct dentry *entry; | 1543 | struct dentry *entry; |
| @@ -1347,6 +1565,15 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer) | |||
| 1347 | pr_warning("Could not create debugfs " | 1565 | pr_warning("Could not create debugfs " |
| 1348 | "'set_ftrace_notrace' entry\n"); | 1566 | "'set_ftrace_notrace' entry\n"); |
| 1349 | 1567 | ||
| 1568 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
| 1569 | entry = debugfs_create_file("set_graph_function", 0444, d_tracer, | ||
| 1570 | NULL, | ||
| 1571 | &ftrace_graph_fops); | ||
| 1572 | if (!entry) | ||
| 1573 | pr_warning("Could not create debugfs " | ||
| 1574 | "'set_graph_function' entry\n"); | ||
| 1575 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
| 1576 | |||
| 1350 | return 0; | 1577 | return 0; |
| 1351 | } | 1578 | } |
| 1352 | 1579 | ||
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 8b6409a62b54..710b39acd81b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -1209,6 +1209,9 @@ int trace_graph_entry(struct ftrace_graph_ent *trace) | |||
| 1209 | int cpu; | 1209 | int cpu; |
| 1210 | int pc; | 1210 | int pc; |
| 1211 | 1211 | ||
| 1212 | if (!ftrace_graph_addr(trace->func)) | ||
| 1213 | return 0; | ||
| 1214 | |||
| 1212 | local_irq_save(flags); | 1215 | local_irq_save(flags); |
| 1213 | cpu = raw_smp_processor_id(); | 1216 | cpu = raw_smp_processor_id(); |
| 1214 | data = tr->data[cpu]; | 1217 | data = tr->data[cpu]; |
| @@ -1217,6 +1220,9 @@ int trace_graph_entry(struct ftrace_graph_ent *trace) | |||
| 1217 | pc = preempt_count(); | 1220 | pc = preempt_count(); |
| 1218 | __trace_graph_entry(tr, data, trace, flags, pc); | 1221 | __trace_graph_entry(tr, data, trace, flags, pc); |
| 1219 | } | 1222 | } |
| 1223 | /* Only do the atomic if it is not already set */ | ||
| 1224 | if (!test_tsk_trace_graph(current)) | ||
| 1225 | set_tsk_trace_graph(current); | ||
| 1220 | atomic_dec(&data->disabled); | 1226 | atomic_dec(&data->disabled); |
| 1221 | local_irq_restore(flags); | 1227 | local_irq_restore(flags); |
| 1222 | 1228 | ||
| @@ -1240,6 +1246,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace) | |||
| 1240 | pc = preempt_count(); | 1246 | pc = preempt_count(); |
| 1241 | __trace_graph_return(tr, data, trace, flags, pc); | 1247 | __trace_graph_return(tr, data, trace, flags, pc); |
| 1242 | } | 1248 | } |
| 1249 | if (!trace->depth) | ||
| 1250 | clear_tsk_trace_graph(current); | ||
| 1243 | atomic_dec(&data->disabled); | 1251 | atomic_dec(&data->disabled); |
| 1244 | local_irq_restore(flags); | 1252 | local_irq_restore(flags); |
| 1245 | } | 1253 | } |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 0565ae9a2210..41f026bfc9ed 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
| @@ -505,13 +505,41 @@ extern unsigned long trace_flags; | |||
| 505 | /* Standard output formatting function used for function return traces */ | 505 | /* Standard output formatting function used for function return traces */ |
| 506 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 506 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
| 507 | extern enum print_line_t print_graph_function(struct trace_iterator *iter); | 507 | extern enum print_line_t print_graph_function(struct trace_iterator *iter); |
| 508 | |||
| 509 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 510 | /* TODO: make this variable */ | ||
| 511 | #define FTRACE_GRAPH_MAX_FUNCS 32 | ||
| 512 | extern int ftrace_graph_count; | ||
| 513 | extern unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS]; | ||
| 514 | |||
| 515 | static inline int ftrace_graph_addr(unsigned long addr) | ||
| 516 | { | ||
| 517 | int i; | ||
| 518 | |||
| 519 | if (!ftrace_graph_count || test_tsk_trace_graph(current)) | ||
| 520 | return 1; | ||
| 521 | |||
| 522 | for (i = 0; i < ftrace_graph_count; i++) { | ||
| 523 | if (addr == ftrace_graph_funcs[i]) | ||
| 524 | return 1; | ||
| 525 | } | ||
| 526 | |||
| 527 | return 0; | ||
| 528 | } | ||
| 508 | #else | 529 | #else |
| 530 | static inline int ftrace_trace_addr(unsigned long addr) | ||
| 531 | { | ||
| 532 | return 1 | ||
| 533 | } | ||
| 534 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
| 535 | |||
| 536 | #else /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
| 509 | static inline enum print_line_t | 537 | static inline enum print_line_t |
| 510 | print_graph_function(struct trace_iterator *iter) | 538 | print_graph_function(struct trace_iterator *iter) |
| 511 | { | 539 | { |
| 512 | return TRACE_TYPE_UNHANDLED; | 540 | return TRACE_TYPE_UNHANDLED; |
| 513 | } | 541 | } |
| 514 | #endif | 542 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ |
| 515 | 543 | ||
| 516 | /* | 544 | /* |
| 517 | * trace_iterator_flags is an enumeration that defines bit | 545 | * trace_iterator_flags is an enumeration that defines bit |
