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