-
Notifications
You must be signed in to change notification settings - Fork 45
/
timetrace.h
221 lines (192 loc) · 6.33 KB
/
timetrace.h
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/* SPDX-License-Identifier: BSD-2-Clause */
#ifndef HOMA_TIMETRACE_H
#define HOMA_TIMETRACE_H
#include <asm/types.h>
#ifdef __UNIT_TEST__
#undef get_cycles
#define get_cycles mock_get_cycles
cycles_t mock_get_cycles(void);
#endif /* __UNIT_TEST__ */
// Change 1 -> 0 in the following line to disable time tracing globally.
// Used only in debugging.
#define ENABLE_TIME_TRACE 1
/**
* Timetrace implements a circular buffer of entries, each of which
* consists of a fine-grain timestamp, a short descriptive string, and
* a few additional values. It's typically used to record times at
* various points in kernel operations, in order to find performance
* bottlenecks. It can record a trace relatively efficiently (< 10ns as
* of 6/2018), and the trace can be retrieved by user programs for
* analysis by reading a file in /proc.
*/
/**
* This structure holds one entry in a tt_buffer.
*/
struct tt_event {
/**
* Time when this event occurred (in tt_rdtsc units).
*/
__u64 timestamp;
/**
* Format string describing the event. NULL means that this
* entry has never been occupied.
*/
const char *format;
/**
* Up to 4 additional arguments that may be referenced by
* @format when printing out this event.
*/
__u32 arg0;
__u32 arg1;
__u32 arg2;
__u32 arg3;
};
/* The number of events in a tt_buffer, as a power of 2. */
#define TT_BUF_SIZE_EXP 14
#define TT_BUF_SIZE BIT(TT_BUF_SIZE_EXP)
/**
* Represents a sequence of events, typically consisting of all those
* generated by one thread. Has a fixed capacity, so slots are re-used
* on a circular basis. This class is not thread-safe.
*/
struct tt_buffer {
/**
* Index within events of the slot to use for the next tt_record call.
*/
int next_index;
/**
* Holds information from the most recent calls to tt_record.
* Updated circularly, so each new event replaces the oldest
* existing event.
*/
struct tt_event events[TT_BUF_SIZE];
};
/**
* Holds information about an attempt to read timetrace information
* using a /proc file. Several of these can exist simultaneously.
*/
struct tt_proc_file {
/* Identifies a particular open file. */
struct file *file;
/* Index of the next entry to return from each tt_buffer. */
int pos[NR_CPUS];
/* Messages are collected here, so they can be dumped out to
* user space in bulk.
*/
#define TT_PF_BUF_SIZE 4000
char msg_storage[TT_PF_BUF_SIZE];
/* Number of bytes in msg_storage currently available to
* copy to application.
*/
int bytes_available;
/* Address of next byte in msg_storage to copy to application. */
char *next_byte;
};
void tt_destroy(void);
void tt_freeze(void);
int tt_init(char *proc_file, int *temp);
void tt_record_buf(struct tt_buffer *buffer, __u64 timestamp,
const char *format, __u32 arg0, __u32 arg1,
__u32 arg2, __u32 arg3);
/* Private methods and variables: exposed so they can be accessed
* by unit tests.
*/
void tt_dbg1(char *msg, ...);
void tt_dbg2(char *msg, ...);
void tt_dbg3(char *msg, ...);
void tt_find_oldest(int *pos);
void tt_get_messages(char *buffer, size_t length);
void tt_print_file(char *path);
void tt_printk(void);
int tt_proc_open(struct inode *inode, struct file *file);
ssize_t tt_proc_read(struct file *file, char __user *user_buf,
size_t length, loff_t *offset);
int tt_proc_release(struct inode *inode, struct file *file);
loff_t tt_proc_lseek(struct file *file, loff_t offset, int whence);
extern struct tt_buffer *tt_buffers[];
extern int tt_buffer_size;
extern atomic_t tt_freeze_count;
extern bool tt_frozen;
extern int tt_pf_storage;
extern bool tt_test_no_khz;
/* Debugging variables exposed by the version of timetrace built into
* the kernel.
*/
extern s64 tt_debug_int64[100];
extern void *tt_debug_ptr[100];
/**
* tt_rdtsc(): return the current value of the fine-grain CPU cycle counter
* (accessed via the RDTSC instruction).
*/
static inline __u64 tt_rdtsc(void)
{
__u32 lo, hi;
__asm__ __volatile__("rdtsc" : "=a" (lo), "=d" (hi));
return (((__u64)hi << 32) | lo);
}
/**
* tt_recordN(): record an event, along with N parameters.
*
* @format: Format string for snprintf that will be used, along with
* arg0..arg3, to generate a human-readable message describing
* what happened, when the time trace is printed. The message
* is generated by calling snprintf as follows:
* snprintf(buffer, size, format, arg0, arg1, arg2, arg3)
* where format and arg0..arg3 are the corresponding arguments
* to this method. This pointer is stored in the buffer, so
* the caller must ensure that its contents will not change
* over its lifetime in the trace.
* @arg0 Argument to use when printing a message about this event.
* @arg1 Argument to use when printing a message about this event.
* @arg2 Argument to use when printing a message about this event.
* @arg3 Argument to use when printing a message about this event.
*/
static inline void tt_record4(const char *format, __u32 arg0, __u32 arg1,
__u32 arg2, __u32 arg3)
{
#if ENABLE_TIME_TRACE
tt_record_buf(tt_buffers[raw_smp_processor_id()], get_cycles(), format,
arg0, arg1, arg2, arg3);
#endif
}
static inline void tt_record3(const char *format, __u32 arg0, __u32 arg1,
__u32 arg2)
{
#if ENABLE_TIME_TRACE
tt_record_buf(tt_buffers[raw_smp_processor_id()], get_cycles(), format,
arg0, arg1, arg2, 0);
#endif
}
static inline void tt_record2(const char *format, __u32 arg0, __u32 arg1)
{
#if ENABLE_TIME_TRACE
tt_record_buf(tt_buffers[raw_smp_processor_id()], get_cycles(), format,
arg0, arg1, 0, 0);
#endif
}
static inline void tt_record1(const char *format, __u32 arg0)
{
#if ENABLE_TIME_TRACE
tt_record_buf(tt_buffers[raw_smp_processor_id()], get_cycles(), format,
arg0, 0, 0, 0);
#endif
}
static inline void tt_record(const char *format)
{
#if ENABLE_TIME_TRACE
tt_record_buf(tt_buffers[raw_smp_processor_id()], get_cycles(), format,
0, 0, 0, 0);
#endif
}
static inline __u32 tt_hi(void *p)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
return ((uintptr_t)p) >> 32;
#pragma GCC diagnostic pop
}
static inline __u32 tt_lo(void *p)
{
return ((uintptr_t)p) & 0xffffffff;
}
#endif // HOMA_TIMETRACE_H