blob: 9acb78b3cc9fb35573a8da916c0dd87fd7cfc3ec [file] [log] [blame]
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -04001/*
2 * kvm trace
3 *
4 * It is designed to allow debugging traces of kvm to be generated
5 * on UP / SMP machines. Each trace entry can be timestamped so that
6 * it's possible to reconstruct a chronological record of trace events.
7 * The implementation refers to blktrace kernel support.
8 *
9 * Copyright (c) 2008 Intel Corporation
10 * Copyright (C) 2006 Jens Axboe <axboe@kernel.dk>
11 *
12 * Authors: Feng(Eric) Liu, eric.e.liu@intel.com
13 *
14 * Date: Feb 2008
15 */
16
17#include <linux/module.h>
18#include <linux/relay.h>
19#include <linux/debugfs.h>
20
21#include <linux/kvm_host.h>
22
23#define KVM_TRACE_STATE_RUNNING (1 << 0)
24#define KVM_TRACE_STATE_PAUSE (1 << 1)
25#define KVM_TRACE_STATE_CLEARUP (1 << 2)
26
27struct kvm_trace {
28 int trace_state;
29 struct rchan *rchan;
30 struct dentry *lost_file;
31 atomic_t lost_records;
32};
33static struct kvm_trace *kvm_trace;
34
35struct kvm_trace_probe {
36 const char *name;
37 const char *format;
38 u32 cycle_in;
39 marker_probe_func *probe_func;
40};
41
42static inline int calc_rec_size(int cycle, int extra)
43{
44 int rec_size = KVM_TRC_HEAD_SIZE;
45
46 rec_size += extra;
47 return cycle ? rec_size += KVM_TRC_CYCLE_SIZE : rec_size;
48}
49
50static void kvm_add_trace(void *probe_private, void *call_data,
51 const char *format, va_list *args)
52{
53 struct kvm_trace_probe *p = probe_private;
54 struct kvm_trace *kt = kvm_trace;
55 struct kvm_trace_rec rec;
56 struct kvm_vcpu *vcpu;
Christian Ehrhardte32c8f22008-07-14 14:00:00 +020057 int i, size;
58 u32 extra;
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -040059
60 if (unlikely(kt->trace_state != KVM_TRACE_STATE_RUNNING))
61 return;
62
Christian Ehrhardte32c8f22008-07-14 14:00:00 +020063 rec.rec_val = TRACE_REC_EVENT_ID(va_arg(*args, u32));
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -040064 vcpu = va_arg(*args, struct kvm_vcpu *);
65 rec.pid = current->tgid;
66 rec.vcpu_id = vcpu->vcpu_id;
67
68 extra = va_arg(*args, u32);
69 WARN_ON(!(extra <= KVM_TRC_EXTRA_MAX));
70 extra = min_t(u32, extra, KVM_TRC_EXTRA_MAX);
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -040071
Christian Ehrhardte32c8f22008-07-14 14:00:00 +020072 rec.rec_val |= TRACE_REC_TCS(p->cycle_in)
73 | TRACE_REC_NUM_DATA_ARGS(extra);
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -040074
Christian Ehrhardte32c8f22008-07-14 14:00:00 +020075 if (p->cycle_in) {
Tan, Li9ef621d2008-05-23 14:54:09 +080076 rec.u.cycle.cycle_u64 = get_cycles();
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -040077
Christian Ehrhardte32c8f22008-07-14 14:00:00 +020078 for (i = 0; i < extra; i++)
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -040079 rec.u.cycle.extra_u32[i] = va_arg(*args, u32);
80 } else {
Christian Ehrhardte32c8f22008-07-14 14:00:00 +020081 for (i = 0; i < extra; i++)
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -040082 rec.u.nocycle.extra_u32[i] = va_arg(*args, u32);
83 }
84
Christian Ehrhardte32c8f22008-07-14 14:00:00 +020085 size = calc_rec_size(p->cycle_in, extra * sizeof(u32));
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -040086 relay_write(kt->rchan, &rec, size);
87}
88
89static struct kvm_trace_probe kvm_trace_probes[] = {
90 { "kvm_trace_entryexit", "%u %p %u %u %u %u %u %u", 1, kvm_add_trace },
91 { "kvm_trace_handler", "%u %p %u %u %u %u %u %u", 0, kvm_add_trace },
92};
93
94static int lost_records_get(void *data, u64 *val)
95{
96 struct kvm_trace *kt = data;
97
98 *val = atomic_read(&kt->lost_records);
99 return 0;
100}
101
102DEFINE_SIMPLE_ATTRIBUTE(kvm_trace_lost_ops, lost_records_get, NULL, "%llu\n");
103
104/*
105 * The relay channel is used in "no-overwrite" mode, it keeps trace of how
106 * many times we encountered a full subbuffer, to tell user space app the
107 * lost records there were.
108 */
109static int kvm_subbuf_start_callback(struct rchan_buf *buf, void *subbuf,
110 void *prev_subbuf, size_t prev_padding)
111{
112 struct kvm_trace *kt;
113
Tan, Li9ef621d2008-05-23 14:54:09 +0800114 if (!relay_buf_full(buf)) {
115 if (!prev_subbuf) {
116 /*
117 * executed only once when the channel is opened
118 * save metadata as first record
119 */
120 subbuf_start_reserve(buf, sizeof(u32));
121 *(u32 *)subbuf = 0x12345678;
122 }
123
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -0400124 return 1;
Tan, Li9ef621d2008-05-23 14:54:09 +0800125 }
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -0400126
127 kt = buf->chan->private_data;
128 atomic_inc(&kt->lost_records);
129
130 return 0;
131}
132
133static struct dentry *kvm_create_buf_file_callack(const char *filename,
134 struct dentry *parent,
135 int mode,
136 struct rchan_buf *buf,
137 int *is_global)
138{
139 return debugfs_create_file(filename, mode, parent, buf,
140 &relay_file_operations);
141}
142
143static int kvm_remove_buf_file_callback(struct dentry *dentry)
144{
145 debugfs_remove(dentry);
146 return 0;
147}
148
149static struct rchan_callbacks kvm_relay_callbacks = {
150 .subbuf_start = kvm_subbuf_start_callback,
151 .create_buf_file = kvm_create_buf_file_callack,
152 .remove_buf_file = kvm_remove_buf_file_callback,
153};
154
155static int do_kvm_trace_enable(struct kvm_user_trace_setup *kuts)
156{
157 struct kvm_trace *kt;
158 int i, r = -ENOMEM;
159
160 if (!kuts->buf_size || !kuts->buf_nr)
161 return -EINVAL;
162
163 kt = kzalloc(sizeof(*kt), GFP_KERNEL);
164 if (!kt)
165 goto err;
166
167 r = -EIO;
168 atomic_set(&kt->lost_records, 0);
Hollis Blanchard76f7c872008-04-15 16:05:42 -0500169 kt->lost_file = debugfs_create_file("lost_records", 0444, kvm_debugfs_dir,
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -0400170 kt, &kvm_trace_lost_ops);
171 if (!kt->lost_file)
172 goto err;
173
Hollis Blanchard76f7c872008-04-15 16:05:42 -0500174 kt->rchan = relay_open("trace", kvm_debugfs_dir, kuts->buf_size,
Feng(Eric) Liud4c9ff22008-04-10 08:47:53 -0400175 kuts->buf_nr, &kvm_relay_callbacks, kt);
176 if (!kt->rchan)
177 goto err;
178
179 kvm_trace = kt;
180
181 for (i = 0; i < ARRAY_SIZE(kvm_trace_probes); i++) {
182 struct kvm_trace_probe *p = &kvm_trace_probes[i];
183
184 r = marker_probe_register(p->name, p->format, p->probe_func, p);
185 if (r)
186 printk(KERN_INFO "Unable to register probe %s\n",
187 p->name);
188 }
189
190 kvm_trace->trace_state = KVM_TRACE_STATE_RUNNING;
191
192 return 0;
193err:
194 if (kt) {
195 if (kt->lost_file)
196 debugfs_remove(kt->lost_file);
197 if (kt->rchan)
198 relay_close(kt->rchan);
199 kfree(kt);
200 }
201 return r;
202}
203
204static int kvm_trace_enable(char __user *arg)
205{
206 struct kvm_user_trace_setup kuts;
207 int ret;
208
209 ret = copy_from_user(&kuts, arg, sizeof(kuts));
210 if (ret)
211 return -EFAULT;
212
213 ret = do_kvm_trace_enable(&kuts);
214 if (ret)
215 return ret;
216
217 return 0;
218}
219
220static int kvm_trace_pause(void)
221{
222 struct kvm_trace *kt = kvm_trace;
223 int r = -EINVAL;
224
225 if (kt == NULL)
226 return r;
227
228 if (kt->trace_state == KVM_TRACE_STATE_RUNNING) {
229 kt->trace_state = KVM_TRACE_STATE_PAUSE;
230 relay_flush(kt->rchan);
231 r = 0;
232 }
233
234 return r;
235}
236
237void kvm_trace_cleanup(void)
238{
239 struct kvm_trace *kt = kvm_trace;
240 int i;
241
242 if (kt == NULL)
243 return;
244
245 if (kt->trace_state == KVM_TRACE_STATE_RUNNING ||
246 kt->trace_state == KVM_TRACE_STATE_PAUSE) {
247
248 kt->trace_state = KVM_TRACE_STATE_CLEARUP;
249
250 for (i = 0; i < ARRAY_SIZE(kvm_trace_probes); i++) {
251 struct kvm_trace_probe *p = &kvm_trace_probes[i];
252 marker_probe_unregister(p->name, p->probe_func, p);
253 }
254
255 relay_close(kt->rchan);
256 debugfs_remove(kt->lost_file);
257 kfree(kt);
258 }
259}
260
261int kvm_trace_ioctl(unsigned int ioctl, unsigned long arg)
262{
263 void __user *argp = (void __user *)arg;
264 long r = -EINVAL;
265
266 if (!capable(CAP_SYS_ADMIN))
267 return -EPERM;
268
269 switch (ioctl) {
270 case KVM_TRACE_ENABLE:
271 r = kvm_trace_enable(argp);
272 break;
273 case KVM_TRACE_PAUSE:
274 r = kvm_trace_pause();
275 break;
276 case KVM_TRACE_DISABLE:
277 r = 0;
278 kvm_trace_cleanup();
279 break;
280 }
281
282 return r;
283}