Completely restructured profiler code cutting all the unused code
[akaros.git] / kern / src / profiler.c
1
2 #include <ros/common.h>
3 #include <smp.h>
4 #include <trap.h>
5 #include <kmalloc.h>
6 #include <atomic.h>
7 #include <sys/types.h>
8 #include "profiler.h"
9
10 struct op_sample {
11         uint64_t hdr;
12         uint64_t event;
13         uint64_t data[0];
14 };
15
16 struct op_entry {
17         struct op_sample *sample;
18         size_t size;
19         uint64_t *data;
20 };
21
22 struct profiler_cpu_context {
23         spinlock_t lock;
24         int tracing;
25         unsigned long sample_received;
26         unsigned long sample_lost_overflow;
27         unsigned long backtrace_aborted;
28         unsigned long sample_invalid_eip;
29         struct block *block;
30 };
31
32 static int profiler_queue_limit = 1024;
33 static size_t profiler_cpu_buffer_size = 65536;
34 static size_t profiler_backtrace_depth = 16;
35 static struct profiler_cpu_context *profiler_percpu_ctx;
36 static struct queue *profiler_queue;
37
38 static inline struct profiler_cpu_context *profiler_get_cpu_ctx(int cpu)
39 {
40         return profiler_percpu_ctx + cpu;
41 }
42
43 static inline uint64_t profiler_create_header(int cpu, size_t nbt)
44 {
45         return (((uint64_t) 0xee01) << 48) | ((uint64_t) cpu << 16) |
46                 (uint64_t) nbt;
47 }
48
49 static inline size_t profiler_cpu_buffer_add_data(struct op_entry *entry,
50                                                                                                   const uintptr_t *values,
51                                                                                                   size_t count)
52 {
53         size_t i, n = entry->size;
54
55         if (unlikely(count > n))
56                 n = count;
57         for (i = 0; i < n; i++)
58                 entry->data[i] = (uint64_t) values[i];
59         entry->size -= n;
60         entry->data += n;
61
62         return entry->size;
63 }
64
65 static void free_cpu_buffers(void)
66 {
67         kfree(profiler_percpu_ctx);
68 }
69
70 static int alloc_cpu_buffers(void)
71 {
72         if (!profiler_queue) {
73                 profiler_queue = qopen(profiler_queue_limit, 0, NULL, NULL);
74                 if (!profiler_queue)
75                         goto fail;
76         }
77
78         /* we *really* don't want to block. Losing data is better. */
79         qdropoverflow(profiler_queue, 1);
80         if (!profiler_percpu_ctx) {
81                 int i;
82
83                 profiler_percpu_ctx =
84                         kzmalloc(sizeof(*profiler_percpu_ctx) * num_cores, KMALLOC_WAIT);
85                 if (!profiler_percpu_ctx)
86                         goto fail;
87
88                 for (i = 0; i < num_cores; i++) {
89                         struct profiler_cpu_context *b = &profiler_percpu_ctx[i];
90
91                         b->tracing = 0;
92                         b->sample_received = 0;
93                         b->sample_lost_overflow = 0;
94                         b->backtrace_aborted = 0;
95                         b->sample_invalid_eip = 0;
96                         spinlock_init_irqsave(&b->lock);
97                 }
98         }
99
100         return 0;
101
102 fail:
103         free_cpu_buffers();
104         return -ENOMEM;
105 }
106
107 int profiler_init(void)
108 {
109         return alloc_cpu_buffers();
110 }
111
112 void profiler_cleanup(void)
113 {
114         free_cpu_buffers();
115 }
116
117 static struct block *profiler_cpu_buffer_write_reserve(
118         struct profiler_cpu_context *cpu_buf, struct op_entry *entry, size_t size)
119 {
120         struct block *b = cpu_buf->block;
121     size_t totalsize = sizeof(struct op_sample) +
122                 size * sizeof(entry->sample->data[0]);
123
124         if (unlikely((!b) || (b->lim - b->wp) < size)) {
125                 if (b)
126                         qibwrite(profiler_queue, b);
127                 /* For now. Later, we will grab a block off the
128                  * emptyblock queue.
129                  */
130                 cpu_buf->block = b = iallocb(profiler_cpu_buffer_size);
131         if (unlikely(!b)) {
132                         printk("%s: fail\n", __func__);
133                         return NULL;
134                 }
135         }
136         entry->sample = (struct op_sample *) b->wp;
137         entry->size = size;
138         entry->data = entry->sample->data;
139
140         b->wp += totalsize;
141
142         return b;
143 }
144
145 static inline int profiler_add_sample(struct profiler_cpu_context *cpu_buf,
146                                                                           uintptr_t pc, unsigned long event)
147 {
148         ERRSTACK(1);
149         struct op_entry entry;
150         struct block *b;
151
152         if (waserror()) {
153                 poperror();
154                 printk("%s: failed\n", __func__);
155                 return 1;
156         }
157
158         b = profiler_cpu_buffer_write_reserve(cpu_buf, &entry, 0);
159         if (likely(b)) {
160                 entry.sample->hdr = profiler_create_header(core_id(), 1);
161                 entry.sample->event = (uint64_t) event;
162                 profiler_cpu_buffer_add_data(&entry, &pc, 1);
163         }
164         poperror();
165
166         return b == NULL;
167 }
168
169 static inline void profiler_begin_trace(struct profiler_cpu_context *cpu_buf)
170 {
171         cpu_buf->tracing = 1;
172 }
173
174 static inline void profiler_end_trace(struct profiler_cpu_context *cpu_buf)
175 {
176         cpu_buf->tracing = 0;
177 }
178
179 static void profiler_cpubuf_flushone(int core, int newbuf)
180 {
181         struct profiler_cpu_context *cpu_buf = profiler_get_cpu_ctx(core);
182
183         spin_lock_irqsave(&cpu_buf->lock);
184         if (cpu_buf->block) {
185                 printk("Core %d has data\n", core);
186                 qibwrite(profiler_queue, cpu_buf->block);
187                 printk("After qibwrite in %s, profiler_queue len %d\n",
188                            __func__, qlen(profiler_queue));
189         }
190         if (newbuf)
191                 cpu_buf->block = iallocb(profiler_cpu_buffer_size);
192         else
193                 cpu_buf->block = NULL;
194         spin_unlock_irqsave(&cpu_buf->lock);
195 }
196
197 void profiler_control_trace(int onoff)
198 {
199         int core;
200         struct profiler_cpu_context *cpu_buf;
201
202         for (core = 0; core < num_cores; core++) {
203                 cpu_buf = profiler_get_cpu_ctx(core);
204                 cpu_buf->tracing = onoff;
205
206                 if (onoff) {
207                         printk("Enable tracing on %d\n", core);
208                         continue;
209                 }
210
211                 /* halting. Force out all buffers. */
212                 profiler_cpubuf_flushone(core, 0);
213         }
214 }
215
216 void profiler_add_trace(uintptr_t pc)
217 {
218         struct profiler_cpu_context *cpu_buf = profiler_get_cpu_ctx(core_id());
219
220         if (profiler_percpu_ctx && cpu_buf->tracing)
221                 profiler_add_sample(cpu_buf, pc, nsec());
222 }
223
224 /* Format for samples:
225  * first word:
226  * high 8 bits is ee, which is an invalid address on amd64.
227  * next 8 bits is protocol version
228  * next 16 bits is unused, MBZ. Later, we can make it a packet type.
229  * next 16 bits is core id
230  * next 8 bits is unused
231  * next 8 bits is # PCs following. This should be at least 1, for one EIP.
232  *
233  * second word is time in ns.
234  *
235  * Third and following words are PCs, there must be at least one of them.
236  */
237 void profiler_add_backtrace(uintptr_t pc, uintptr_t fp)
238 {
239         int cpu = core_id();
240         struct profiler_cpu_context *cpu_buf = profiler_get_cpu_ctx(cpu);
241
242         if (profiler_percpu_ctx && cpu_buf->tracing) {
243                 struct op_entry entry;
244                 struct block *b;
245                 uintptr_t bt_pcs[profiler_backtrace_depth];
246                 size_t n = backtrace_list(pc, fp, bt_pcs, profiler_backtrace_depth);
247
248                 /* write_reserve always assumes passed-in-size + 2.
249                  * backtrace_depth should always be > 0.
250                  */
251                 b = profiler_cpu_buffer_write_reserve(cpu_buf, &entry, n);
252                 if (likely(b)) {
253                         entry.sample->hdr = profiler_create_header(cpu, n);
254                         entry.sample->event = nsec();
255                         profiler_cpu_buffer_add_data(&entry, bt_pcs, n);
256                 }
257         }
258 }
259
260 void profiler_add_userpc(uintptr_t pc)
261 {
262         int cpu = core_id();
263         struct profiler_cpu_context *cpu_buf = profiler_get_cpu_ctx(cpu);
264
265         if (profiler_percpu_ctx && cpu_buf->tracing) {
266                 struct op_entry entry;
267                 struct block *b = profiler_cpu_buffer_write_reserve(cpu_buf,
268                                                                                                                         &entry, 1);
269
270                 if (likely(b)) {
271                         entry.sample->hdr = profiler_create_header(cpu, 1);
272                         entry.sample->event = nsec();
273                         profiler_cpu_buffer_add_data(&entry, &pc, 1);
274                 }
275         }
276 }
277
278 void profiler_add_hw_sample(struct hw_trapframe *hw_tf)
279 {
280         if (in_kernel(hw_tf))
281                 profiler_add_backtrace(get_hwtf_pc(hw_tf), get_hwtf_fp(hw_tf));
282         else
283                 profiler_add_userpc(get_hwtf_pc(hw_tf));
284 }
285
286 int profiler_size(void)
287 {
288         return qlen(profiler_queue);
289 }
290
291 int profiler_read(void *va, int n)
292 {
293         struct profiler_cpu_context *cpu_buf = profiler_get_cpu_ctx(core_id());
294
295         return cpu_buf->tracing ? qread(profiler_queue, va, n) : 0;
296 }