1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <[email protected]>
4 *
5 */
6 #include <stdio.h>
7 #include "trace-cmd.h"
8 #include "trace-local.h"
9
10 static const char blk_event_start[] =
11 "name: blktrace\n"
12 "ID: %d\n"
13 "format:\n"
14 "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\n"
15 "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\n"
16 "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\n"
17 "\tfield:int common_pid;\toffset:4;\tsize:4;\n";
18
19 static const char blk_body[] = "\n"
20 "\tfield:u64 sector;\toffset:16;\tsize:8;\n"
21 "\tfield:int bytes;\toffset:24;\tsize:4;\n"
22 "\tfield:int action;\toffset:28;\tsize:4;\n"
23 "\tfield:int pid;\toffset:32;\tsize:4;\n"
24 "\tfield:int device;\toffset:36;\tsize:4;\n"
25 "\tfield:int cpu;\toffset:40;\tsize:4;\n"
26 "\tfield:short error;\toffset:44;\tsize:2;\n"
27 "\tfield:short pdu_len;\toffset:46;\tsize:2;\n"
28 "\tfield:void data;\toffset:48;\tsize:0;\n"
29 "\n"
30 "print fmt: \"%%d\", REC->pid\n";
31
tracecmd_blk_hack(struct tracecmd_input * handle)32 int tracecmd_blk_hack(struct tracecmd_input *handle)
33 {
34 struct tep_handle *pevent;
35 struct tep_event *event;
36 struct tep_format_field *field;
37 char buf[4096]; /* way more than enough! */
38 int id;
39 int l;
40 int r;
41
42 pevent = tracecmd_get_tep(handle);
43
44 /*
45 * Unfortunately, the TRACE_BLK has changed a bit.
46 * We need to test if various events exist to try
47 * to guess what event id TRACE_BLK would be.
48 */
49
50 /* It was originally behind the "power" event */
51 event = tep_find_event_by_name(pevent, "ftrace", "power");
52 if (event) {
53 id = event->id + 1;
54 goto found;
55 }
56
57 /*
58 * But the power tracer is now in perf.
59 * Then it was after kmem_free
60 */
61 event = tep_find_event_by_name(pevent, "ftrace", "kmem_free");
62 if (event) {
63 id = event->id + 1;
64 goto found;
65 }
66
67 /*
68 * But that then went away.
69 * Currently it should be behind the user stack.
70 */
71 event = tep_find_event_by_name(pevent, "ftrace", "user_stack");
72 if (event) {
73 id = event->id + 1;
74 goto found;
75 }
76 /* Give up :( */
77 return -1;
78
79 found:
80 /*
81 * Blk events are not exported in the events directory.
82 * This is a hack to attempt to create a block event
83 * that we can read.
84 *
85 * We'll make a format file to look like this:
86 *
87 * name: blktrace
88 * ID: 13
89 * format:
90 * field:unsigned short common_type; offset:0; size:2;
91 * field:unsigned char common_flags; offset:2; size:1;
92 * field:unsigned char common_preempt_count; offset:3; size:1;
93 * field:int common_pid; offset:4; size:4;
94 * field:int common_lock_depth; offset:8; size:4;
95 *
96 * field:u64 sector; offset:16; size:8;
97 * field:int bytes; offset:32; size:4;
98 * field:int action; offset:36; size:4;
99 * field:int pid; offset:40; size:4;
100 * field:int device; offset:44; size:4;
101 * field:int cpu; offset:48; size:4;
102 * field:short error; offset:52; size:2;
103 * field:short pdu_len; offset:54; size:2;
104 * field:void data; offset:60; size:0;
105 *
106 * print fmt: "%d", REC->pid
107 *
108 * Note: the struct blk_io_trace is used directly and
109 * just the first parts of the struct are not used in order
110 * to not write over the ftrace data.
111 */
112
113 /* Make sure the common fields exist */
114 field = tep_find_common_field(event, "common_type");
115 if (!field || field->offset != 0 || field->size != 2)
116 goto fail;
117 field = tep_find_common_field(event, "common_flags");
118 if (!field || field->offset != 2 || field->size != 1)
119 goto fail;
120 field = tep_find_common_field(event, "common_preempt_count");
121 if (!field || field->offset != 3 || field->size != 1)
122 goto fail;
123 field = tep_find_common_field(event, "common_pid");
124 if (!field || field->offset != 4 || field->size != 4)
125 goto fail;
126 r = sprintf(buf, blk_event_start, id);
127 l = r;
128
129 /* lock depth is optional */
130 field = tep_find_common_field(event, "common_lock_depth");
131 if (field) {
132 if (field->offset != 8 || field->size != 4)
133 return -1;
134 r = sprintf(buf+l, "\tfield:int common_lock_depth;\toffset:8;\tsize:4;\n");
135 l += r;
136 }
137
138 r = sprintf(buf+l, blk_body);
139
140 /* Parse this event */
141 l += r;
142 tep_parse_event(pevent, buf, l, "ftrace");
143
144 return 0;
145
146 fail:
147 return -1;
148 }
149