aboutsummaryrefslogtreecommitdiff
path: root/tools/lib/traceevent/plugins/plugin_futex.c
blob: 5d76df141401e5cdcea5ba0cb068e5e51297b22f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 * Copyright (C) 2017 National Instruments Corp.
 *
 * Author: Julia Cartwright <[email protected]>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License (not later!)
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not,  see <http://www.gnu.org/licenses>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/futex.h>

#include "event-parse.h"

#define ARRAY_SIZE(_a) (sizeof(_a) / sizeof((_a)[0]))

struct futex_args {
	unsigned long long	uaddr;
	unsigned long long	op;
	unsigned long long	val;
	unsigned long long	utime; /* or val2 */
	unsigned long long	uaddr2;
	unsigned long long	val3;
};

struct futex_op {
	const char	*name;
	const char	*fmt_val;
	const char	*fmt_utime;
	const char	*fmt_uaddr2;
	const char	*fmt_val3;
};

static const struct futex_op futex_op_tbl[] = {
	{            "FUTEX_WAIT", " val=0x%08llx", " utime=0x%08llx",               NULL,             NULL },
	{            "FUTEX_WAKE",     " val=%llu",              NULL,               NULL,             NULL },
	{              "FUTEX_FD",     " val=%llu",              NULL,               NULL,             NULL },
	{         "FUTEX_REQUEUE",     " val=%llu",      " val2=%llu", " uaddr2=0x%08llx",             NULL },
	{     "FUTEX_CMP_REQUEUE",     " val=%llu",      " val2=%llu", " uaddr2=0x%08llx", " val3=0x%08llx" },
	{         "FUTEX_WAKE_OP",     " val=%llu",      " val2=%llu", " uaddr2=0x%08llx", " val3=0x%08llx" },
	{         "FUTEX_LOCK_PI",            NULL, " utime=0x%08llx",               NULL,             NULL },
	{       "FUTEX_UNLOCK_PI",            NULL,              NULL,               NULL,             NULL },
	{      "FUTEX_TRYLOCK_PI",            NULL,              NULL,               NULL,             NULL },
	{     "FUTEX_WAIT_BITSET", " val=0x%08llx", " utime=0x%08llx",               NULL, " val3=0x%08llx" },
	{     "FUTEX_WAKE_BITSET",     " val=%llu",              NULL,               NULL, " val3=0x%08llx" },
	{ "FUTEX_WAIT_REQUEUE_PI", " val=0x%08llx", " utime=0x%08llx", " uaddr2=0x%08llx", " val3=0x%08llx" },
	{  "FUTEX_CMP_REQUEUE_PI",     " val=%llu",      " val2=%llu", " uaddr2=0x%08llx", " val3=0x%08llx" },
};


static void futex_print(struct trace_seq *s, const struct futex_args *args,
			const struct futex_op *fop)
{
	trace_seq_printf(s, " uaddr=0x%08llx", args->uaddr);

	if (fop->fmt_val)
		trace_seq_printf(s, fop->fmt_val, args->val);

	if (fop->fmt_utime)
		trace_seq_printf(s,fop->fmt_utime, args->utime);

	if (fop->fmt_uaddr2)
		trace_seq_printf(s, fop->fmt_uaddr2, args->uaddr2);

	if (fop->fmt_val3)
		trace_seq_printf(s, fop->fmt_val3, args->val3);
}

static int futex_handler(struct trace_seq *s, struct tep_record *record,
			 struct tep_event *event, void *context)
{
	const struct futex_op *fop;
	struct futex_args args;
	unsigned long long cmd;

	if (tep_get_field_val(s, event, "uaddr", record, &args.uaddr, 1))
		return 1;

	if (tep_get_field_val(s, event, "op", record, &args.op, 1))
		return 1;

	if (tep_get_field_val(s, event, "val", record, &args.val, 1))
		return 1;

	if (tep_get_field_val(s, event, "utime", record, &args.utime, 1))
		return 1;

	if (tep_get_field_val(s, event, "uaddr2", record, &args.uaddr2, 1))
		return 1;

	if (tep_get_field_val(s, event, "val3", record, &args.val3, 1))
		return 1;

	cmd = args.op & FUTEX_CMD_MASK;
	if (cmd >= ARRAY_SIZE(futex_op_tbl))
		return 1;

	fop = &futex_op_tbl[cmd];

	trace_seq_printf(s, "op=%s", fop->name);

	if (args.op & FUTEX_PRIVATE_FLAG)
		trace_seq_puts(s, "|FUTEX_PRIVATE_FLAG");

	if (args.op & FUTEX_CLOCK_REALTIME)
		trace_seq_puts(s, "|FUTEX_CLOCK_REALTIME");

	futex_print(s, &args, fop);
	return 0;
}

int TEP_PLUGIN_LOADER(struct tep_handle *tep)
{
	tep_register_event_handler(tep, -1, "syscalls", "sys_enter_futex",
				   futex_handler, NULL);
	return 0;
}

void TEP_PLUGIN_UNLOADER(struct tep_handle *tep)
{
	tep_unregister_event_handler(tep, -1, "syscalls", "sys_enter_futex",
				     futex_handler, NULL);
}