2 Copyright (c) 2010 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <vppinfra/error.h>
18 #include <vppinfra/unix.h>
19 #include <vppinfra/elog.h>
20 #include <vppinfra/format.h>
21 #include <vppinfra/os.h>
23 #include <sys/types.h>
40 sched_event_type_t type;
43 void kelog_init (elog_main_t * em, char * kernel_tracer, u32 n_events)
45 int enable_fd, current_tracer_fd, data_fd;
47 struct timespec ts, ts2;
48 char *trace_enable = "/debug/tracing/tracing_enabled";
49 char *current_tracer = "/debug/tracing/current_tracer";
50 char *trace_data = "/debug/tracing/trace";
51 f64 realtime, monotonic;
52 f64 freq, secs_per_clock;
54 ASSERT (kernel_tracer);
59 /* init first so we won't hurt ourselves if we bail */
60 elog_init (em, n_events);
62 enable_fd = open (trace_enable, O_RDWR);
65 clib_warning ("Couldn't open %s", trace_enable);
68 /* disable kernel tracing */
69 if (write (enable_fd, "0\n", 2) != 2)
71 clib_unix_warning ("disable tracing");
77 * open + clear the data buffer.
78 * see .../linux/kernel/trace/trace.c:tracing_open()
80 data_fd = open (trace_data, O_RDWR | O_TRUNC);
83 clib_warning ("Couldn't open+clear %s", trace_data);
88 /* configure tracing */
89 current_tracer_fd = open (current_tracer, O_RDWR);
91 if (current_tracer_fd < 0)
93 clib_warning ("Couldn't open %s", current_tracer);
98 len = strlen(kernel_tracer);
100 if (write (current_tracer_fd, kernel_tracer, len) != len)
102 clib_unix_warning ("configure trace");
103 close(current_tracer_fd);
108 close(current_tracer_fd);
111 * The kernel event log uses CLOCK_MONOTONIC timestamps,
112 * not CLOCK_REALTIME timestamps. These differ by a constant
113 * but the constant is not available in user mode.
114 * This estimate will be off by one syscall round-trip.
116 clib_time_init (&em->cpu_timer);
117 em->init_time.cpu = em->cpu_timer.init_cpu_time;
118 syscall (SYS_clock_gettime, CLOCK_MONOTONIC, &ts);
120 /* enable kernel tracing */
121 if (write (enable_fd, "1\n", 2) != 2)
123 clib_unix_warning ("enable tracing");
132 u8 *format_sched_event (u8 * s, va_list * va)
134 sched_event_t *e = va_arg (*va, sched_event_t *);
136 s = format (s, "cpu %d task %10s type %s timestamp %12.6f\n",
137 e->cpu, e->task, e->type ? "WAKEUP " : "RUNNING", e->timestamp);
142 sched_event_t *parse_sched_switch_trace (u8 *tdata, u32 *index)
144 u8 *cp = tdata + *index;
145 u8 *limit = tdata + vec_len(tdata);
147 static sched_event_t event;
148 sched_event_t *e = &event;
149 static u8 *task_name;
154 /* eat leading w/s */
155 while (cp < limit && (*cp == ' ' && *cp == '\t'))
163 while (cp < limit && (*cp != '\n'))
170 clib_warning ("bugger 0");
174 while (cp < limit && *cp != ']')
182 clib_warning ("bugger 0.1");
187 while (cp < limit && (*cp == ' ' && *cp == '\t'))
191 clib_warning ("bugger 0.2");
197 while (cp < limit && (*cp != '.'))
202 clib_warning ("bugger 0.3");
210 e->timestamp = ((f64)secs) + ((f64)usecs)*1e-6;
212 /* eat up to third colon */
213 for (i = 0; i < 3; i++)
215 while (cp < limit && *cp != ':')
222 clib_warning ("bugger 1");
225 /* aim at '>' (switch-to) / '+' (wakeup) */
229 clib_warning ("bugger 2");
238 clib_warning ("bugger 3");
245 clib_warning ("bugger 4");
254 clib_warning ("bugger 4");
257 while (cp < limit && (*cp == ' ' || *cp == '\t'))
262 for (i = 0; i < 2; i++)
264 while (cp < limit && *cp != ':')
271 clib_warning ("bugger 5");
278 clib_warning ("bugger 6");
281 while (cp < limit && (*cp != ' ' && *cp != '\n'))
283 vec_add1(task_name, *cp);
286 vec_add1(task_name, 0);
287 /* _vec_len() = 0 in caller */
297 static u32 elog_id_for_pid (elog_main_t *em, u8 *name, u32 pid)
300 mhash_t * h = &em->string_table_hash;
302 if (! em->string_table_hash.hash)
303 mhash_init (h, sizeof (uword), sizeof (pid));
305 p = mhash_get (h, &pid);
308 r = elog_string (em, "%s(%d)", name, pid);
309 mhash_set (h, &pid, r, /* old_value */ 0);
313 void kelog_collect_sched_switch_trace (elog_main_t *em)
315 int enable_fd, data_fd;
316 char *trace_enable = "/debug/tracing/tracing_enabled";
317 char *trace_data = "/debug/tracing/trace";
320 int bytes, total_bytes;
327 enable_fd = open (trace_enable, O_RDWR);
330 clib_warning ("Couldn't open %s", trace_enable);
333 /* disable kernel tracing */
334 if (write (enable_fd, "0\n", 2) != 2)
336 clib_unix_warning ("disable tracing");
342 /* Read the trace data */
343 data_fd = open (trace_data, O_RDWR);
346 clib_warning ("Couldn't open %s", trace_data);
351 * Extract trace into a vector. Note that seq_printf() [kernel]
352 * is not guaranteed to produce 4096 bytes at a time.
354 vec_validate (data, 4095);
359 bytes = read(data_fd, data+pos, 4096);
363 total_bytes += bytes;
364 _vec_len(data) = total_bytes;
367 vec_validate(data, vec_len(data)+4095);
371 /* Synthesize events */
375 while ((evt = parse_sched_switch_trace (data, &index)))
379 fake_cpu_clock = evt->timestamp * em->cpu_timer.clocks_per_second;
381 ELOG_TYPE_DECLARE (e) =
383 .format = "%d: %s %s",
384 .format_args = "i4T4t4",
386 .enum_strings = { "running", "wakeup", },
388 struct { u32 cpu, string_table_offset, which; } * ed;
390 ed = elog_event_data_not_inline (em, &__ELOG_TYPE_VAR(e),
394 ed->string_table_offset = elog_id_for_pid (em, evt->task, evt->pid);
395 ed->which = evt->type;
397 _vec_len(evt->task) = 0;