Skip to content

Commit 59df055

Browse files
author
Steven Rostedt
committed
ftrace: trace different functions with a different tracer
Impact: new feature Currently, the function tracer only gives you an ability to hook a tracer to all functions being traced. The dynamic function trace allows you to pick and choose which of those functions will be traced, but all functions being traced will call all tracers that registered with the function tracer. This patch adds a new feature that allows a tracer to hook to specific functions, even when all functions are being traced. It allows for different functions to call different tracer hooks. The way this is accomplished is by a special function that will hook to the function tracer and will set up a hash table knowing which tracer hook to call with which function. This is the most general and easiest method to accomplish this. Later, an arch may choose to supply their own method in changing the mcount call of a function to call a different tracer. But that will be an exercise for the future. To register a function: struct ftrace_hook_ops { void (*func)(unsigned long ip, unsigned long parent_ip, void **data); int (*callback)(unsigned long ip, void **data); void (*free)(void **data); }; int register_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops, void *data); glob is a simple glob to search for the functions to hook. ops is a pointer to the operations (listed below) data is the default data to be passed to the hook functions when traced ops: func is the hook function to call when the functions are traced callback is a callback function that is called when setting up the hash. That is, if the tracer needs to do something special for each function, that is being traced, and wants to give each function its own data. The address of the entry data is passed to this callback, so that the callback may wish to update the entry to whatever it would like. free is a callback for when the entry is freed. In case the tracer allocated any data, it is give the chance to free it. To unregister we have three functions: void unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops, void *data) This will unregister all hooks that match glob, point to ops, and have its data matching data. (note, if glob is NULL, blank or '*', all functions will be tested). void unregister_ftrace_function_hook_func(char *glob, struct ftrace_hook_ops *ops) This will unregister all functions matching glob that has an entry pointing to ops. void unregister_ftrace_function_hook_all(char *glob) This simply unregisters all funcs. Signed-off-by: Steven Rostedt <srostedt@redhat.com>
1 parent e6ea44e commit 59df055

File tree

2 files changed

+265
-0
lines changed

2 files changed

+265
-0
lines changed

include/linux/ftrace.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,24 @@ struct ftrace_func_command {
106106
/* asm/ftrace.h must be defined for archs supporting dynamic ftrace */
107107
#include <asm/ftrace.h>
108108

109+
struct ftrace_hook_ops {
110+
void (*func)(unsigned long ip,
111+
unsigned long parent_ip,
112+
void **data);
113+
int (*callback)(unsigned long ip, void **data);
114+
void (*free)(void **data);
115+
};
116+
117+
extern int
118+
register_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
119+
void *data);
120+
extern void
121+
unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
122+
void *data);
123+
extern void
124+
unregister_ftrace_function_hook_func(char *glob, struct ftrace_hook_ops *ops);
125+
extern void unregister_ftrace_function_hook_all(char *glob);
126+
109127
enum {
110128
FTRACE_FL_FREE = (1 << 0),
111129
FTRACE_FL_FAILED = (1 << 1),

kernel/trace/ftrace.c

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/sysctl.h>
2828
#include <linux/ctype.h>
2929
#include <linux/list.h>
30+
#include <linux/hash.h>
3031

3132
#include <asm/ftrace.h>
3233

@@ -1245,6 +1246,252 @@ static int __init ftrace_mod_cmd_init(void)
12451246
}
12461247
device_initcall(ftrace_mod_cmd_init);
12471248

1249+
#define FTRACE_HASH_BITS 7
1250+
#define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS)
1251+
static struct hlist_head ftrace_func_hash[FTRACE_FUNC_HASHSIZE] __read_mostly;
1252+
1253+
struct ftrace_func_hook {
1254+
struct hlist_node node;
1255+
struct ftrace_hook_ops *ops;
1256+
unsigned long flags;
1257+
unsigned long ip;
1258+
void *data;
1259+
struct rcu_head rcu;
1260+
};
1261+
1262+
static void
1263+
function_trace_hook_call(unsigned long ip, unsigned long parent_ip)
1264+
{
1265+
struct ftrace_func_hook *entry;
1266+
struct hlist_head *hhd;
1267+
struct hlist_node *n;
1268+
unsigned long key;
1269+
int resched;
1270+
1271+
key = hash_long(ip, FTRACE_HASH_BITS);
1272+
1273+
hhd = &ftrace_func_hash[key];
1274+
1275+
if (hlist_empty(hhd))
1276+
return;
1277+
1278+
/*
1279+
* Disable preemption for these calls to prevent a RCU grace
1280+
* period. This syncs the hash iteration and freeing of items
1281+
* on the hash. rcu_read_lock is too dangerous here.
1282+
*/
1283+
resched = ftrace_preempt_disable();
1284+
hlist_for_each_entry_rcu(entry, n, hhd, node) {
1285+
if (entry->ip == ip)
1286+
entry->ops->func(ip, parent_ip, &entry->data);
1287+
}
1288+
ftrace_preempt_enable(resched);
1289+
}
1290+
1291+
static struct ftrace_ops trace_hook_ops __read_mostly =
1292+
{
1293+
.func = function_trace_hook_call,
1294+
};
1295+
1296+
static int ftrace_hook_registered;
1297+
1298+
static void __enable_ftrace_function_hook(void)
1299+
{
1300+
int i;
1301+
1302+
if (ftrace_hook_registered)
1303+
return;
1304+
1305+
for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
1306+
struct hlist_head *hhd = &ftrace_func_hash[i];
1307+
if (hhd->first)
1308+
break;
1309+
}
1310+
/* Nothing registered? */
1311+
if (i == FTRACE_FUNC_HASHSIZE)
1312+
return;
1313+
1314+
__register_ftrace_function(&trace_hook_ops);
1315+
ftrace_startup(0);
1316+
ftrace_hook_registered = 1;
1317+
}
1318+
1319+
static void __disable_ftrace_function_hook(void)
1320+
{
1321+
int i;
1322+
1323+
if (!ftrace_hook_registered)
1324+
return;
1325+
1326+
for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
1327+
struct hlist_head *hhd = &ftrace_func_hash[i];
1328+
if (hhd->first)
1329+
return;
1330+
}
1331+
1332+
/* no more funcs left */
1333+
__unregister_ftrace_function(&trace_hook_ops);
1334+
ftrace_shutdown(0);
1335+
ftrace_hook_registered = 0;
1336+
}
1337+
1338+
1339+
static void ftrace_free_entry_rcu(struct rcu_head *rhp)
1340+
{
1341+
struct ftrace_func_hook *entry =
1342+
container_of(rhp, struct ftrace_func_hook, rcu);
1343+
1344+
if (entry->ops->free)
1345+
entry->ops->free(&entry->data);
1346+
kfree(entry);
1347+
}
1348+
1349+
1350+
int
1351+
register_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
1352+
void *data)
1353+
{
1354+
struct ftrace_func_hook *entry;
1355+
struct ftrace_page *pg;
1356+
struct dyn_ftrace *rec;
1357+
unsigned long key;
1358+
int type, len, not;
1359+
int count = 0;
1360+
char *search;
1361+
1362+
type = ftrace_setup_glob(glob, strlen(glob), &search, &not);
1363+
len = strlen(search);
1364+
1365+
/* we do not support '!' for function hooks */
1366+
if (WARN_ON(not))
1367+
return -EINVAL;
1368+
1369+
mutex_lock(&ftrace_lock);
1370+
do_for_each_ftrace_rec(pg, rec) {
1371+
1372+
if (rec->flags & FTRACE_FL_FAILED)
1373+
continue;
1374+
1375+
if (!ftrace_match_record(rec, search, len, type))
1376+
continue;
1377+
1378+
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
1379+
if (!entry) {
1380+
/* If we did not hook to any, then return error */
1381+
if (!count)
1382+
count = -ENOMEM;
1383+
goto out_unlock;
1384+
}
1385+
1386+
count++;
1387+
1388+
entry->data = data;
1389+
1390+
/*
1391+
* The caller might want to do something special
1392+
* for each function we find. We call the callback
1393+
* to give the caller an opportunity to do so.
1394+
*/
1395+
if (ops->callback) {
1396+
if (ops->callback(rec->ip, &entry->data) < 0) {
1397+
/* caller does not like this func */
1398+
kfree(entry);
1399+
continue;
1400+
}
1401+
}
1402+
1403+
entry->ops = ops;
1404+
entry->ip = rec->ip;
1405+
1406+
key = hash_long(entry->ip, FTRACE_HASH_BITS);
1407+
hlist_add_head_rcu(&entry->node, &ftrace_func_hash[key]);
1408+
1409+
} while_for_each_ftrace_rec();
1410+
__enable_ftrace_function_hook();
1411+
1412+
out_unlock:
1413+
mutex_unlock(&ftrace_lock);
1414+
1415+
return count;
1416+
}
1417+
1418+
enum {
1419+
HOOK_TEST_FUNC = 1,
1420+
HOOK_TEST_DATA = 2
1421+
};
1422+
1423+
static void
1424+
__unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
1425+
void *data, int flags)
1426+
{
1427+
struct ftrace_func_hook *entry;
1428+
struct hlist_node *n, *tmp;
1429+
char str[KSYM_SYMBOL_LEN];
1430+
int type = MATCH_FULL;
1431+
int i, len = 0;
1432+
char *search;
1433+
1434+
if (glob && (strcmp(glob, "*") || !strlen(glob)))
1435+
glob = NULL;
1436+
else {
1437+
int not;
1438+
1439+
type = ftrace_setup_glob(glob, strlen(glob), &search, &not);
1440+
len = strlen(search);
1441+
1442+
/* we do not support '!' for function hooks */
1443+
if (WARN_ON(not))
1444+
return;
1445+
}
1446+
1447+
mutex_lock(&ftrace_lock);
1448+
for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
1449+
struct hlist_head *hhd = &ftrace_func_hash[i];
1450+
1451+
hlist_for_each_entry_safe(entry, n, tmp, hhd, node) {
1452+
1453+
/* break up if statements for readability */
1454+
if ((flags & HOOK_TEST_FUNC) && entry->ops != ops)
1455+
continue;
1456+
1457+
if ((flags & HOOK_TEST_DATA) && entry->data != data)
1458+
continue;
1459+
1460+
/* do this last, since it is the most expensive */
1461+
if (glob) {
1462+
kallsyms_lookup(entry->ip, NULL, NULL,
1463+
NULL, str);
1464+
if (!ftrace_match(str, glob, len, type))
1465+
continue;
1466+
}
1467+
1468+
hlist_del(&entry->node);
1469+
call_rcu(&entry->rcu, ftrace_free_entry_rcu);
1470+
}
1471+
}
1472+
__disable_ftrace_function_hook();
1473+
mutex_unlock(&ftrace_lock);
1474+
}
1475+
1476+
void
1477+
unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
1478+
void *data)
1479+
{
1480+
__unregister_ftrace_function_hook(glob, ops, data,
1481+
HOOK_TEST_FUNC | HOOK_TEST_DATA);
1482+
}
1483+
1484+
void
1485+
unregister_ftrace_function_hook_func(char *glob, struct ftrace_hook_ops *ops)
1486+
{
1487+
__unregister_ftrace_function_hook(glob, ops, NULL, HOOK_TEST_FUNC);
1488+
}
1489+
1490+
void unregister_ftrace_function_hook_all(char *glob)
1491+
{
1492+
__unregister_ftrace_function_hook(glob, NULL, NULL, 0);
1493+
}
1494+
12481495
static LIST_HEAD(ftrace_commands);
12491496
static DEFINE_MUTEX(ftrace_cmd_mutex);
12501497

0 commit comments

Comments
 (0)