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;
44 kelog_init (elog_main_t * em, char *kernel_tracer, u32 n_events)
46 int enable_fd, current_tracer_fd, data_fd;
48 struct timespec ts, ts2;
49 char *trace_enable = "/debug/tracing/tracing_enabled";
50 char *current_tracer = "/debug/tracing/current_tracer";
51 char *trace_data = "/debug/tracing/trace";
52 f64 realtime, monotonic;
53 f64 freq, secs_per_clock;
55 ASSERT (kernel_tracer);
60 /* init first so we won't hurt ourselves if we bail */
61 elog_init (em, n_events);
63 enable_fd = open (trace_enable, O_RDWR);
66 clib_warning ("Couldn't open %s", trace_enable);
69 /* disable kernel tracing */
70 if (write (enable_fd, "0\n", 2) != 2)
72 clib_unix_warning ("disable tracing");
78 * open + clear the data buffer.
79 * see .../linux/kernel/trace/trace.c:tracing_open()
81 data_fd = open (trace_data, O_RDWR | O_TRUNC);
84 clib_warning ("Couldn't open+clear %s", trace_data);
89 /* configure tracing */
90 current_tracer_fd = open (current_tracer, O_RDWR);
92 if (current_tracer_fd < 0)
94 clib_warning ("Couldn't open %s", current_tracer);
99 len = strlen (kernel_tracer);
101 if (write (current_tracer_fd, kernel_tracer, len) != len)
103 clib_unix_warning ("configure trace");
104 close (current_tracer_fd);
109 close (current_tracer_fd);
112 * The kernel event log uses CLOCK_MONOTONIC timestamps,
113 * not CLOCK_REALTIME timestamps. These differ by a constant
114 * but the constant is not available in user mode.
115 * This estimate will be off by one syscall round-trip.
117 clib_time_init (&em->cpu_timer);
118 em->init_time.cpu = em->cpu_timer.init_cpu_time;
119 syscall (SYS_clock_gettime, CLOCK_MONOTONIC, &ts);
121 /* enable kernel tracing */
122 if (write (enable_fd, "1\n", 2) != 2)
124 clib_unix_warning ("enable tracing");
134 format_sched_event (u8 * s, va_list * va)
136 sched_event_t *e = va_arg (*va, sched_event_t *);
138 s = format (s, "cpu %d task %10s type %s timestamp %12.6f\n",
139 e->cpu, e->task, e->type ? "WAKEUP " : "RUNNING", e->timestamp);
145 parse_sched_switch_trace (u8 * tdata, u32 * index)
147 u8 *cp = tdata + *index;
148 u8 *limit = tdata + vec_len (tdata);
150 static sched_event_t event;
151 sched_event_t *e = &event;
152 static u8 *task_name;
157 /* eat leading w/s */
158 while (cp < limit && (*cp == ' ' && *cp == '\t'))
166 while (cp < limit && (*cp != '\n'))
173 clib_warning ("bugger 0");
177 while (cp < limit && *cp != ']')
185 clib_warning ("bugger 0.1");
190 while (cp < limit && (*cp == ' ' && *cp == '\t'))
194 clib_warning ("bugger 0.2");
200 while (cp < limit && (*cp != '.'))
205 clib_warning ("bugger 0.3");
213 e->timestamp = ((f64) secs) + ((f64) usecs) * 1e-6;
215 /* eat up to third colon */
216 for (i = 0; i < 3; i++)
218 while (cp < limit && *cp != ':')
225 clib_warning ("bugger 1");
228 /* aim at '>' (switch-to) / '+' (wakeup) */
232 clib_warning ("bugger 2");
241 clib_warning ("bugger 3");
248 clib_warning ("bugger 4");
257 clib_warning ("bugger 4");
260 while (cp < limit && (*cp == ' ' || *cp == '\t'))
265 for (i = 0; i < 2; i++)
267 while (cp < limit && *cp != ':')
274 clib_warning ("bugger 5");
281 clib_warning ("bugger 6");
284 while (cp < limit && (*cp != ' ' && *cp != '\n'))
286 vec_add1 (task_name, *cp);
289 vec_add1 (task_name, 0);
290 /* _vec_len() = 0 in caller */
301 elog_id_for_pid (elog_main_t * em, u8 * name, u32 pid)
304 mhash_t *h = &em->string_table_hash;
306 if (!em->string_table_hash.hash)
307 mhash_init (h, sizeof (uword), sizeof (pid));
309 p = mhash_get (h, &pid);
312 r = elog_string (em, "%s(%d)", name, pid);
313 mhash_set (h, &pid, r, /* old_value */ 0);
318 kelog_collect_sched_switch_trace (elog_main_t * em)
320 int enable_fd, data_fd;
321 char *trace_enable = "/debug/tracing/tracing_enabled";
322 char *trace_data = "/debug/tracing/trace";
325 int bytes, total_bytes;
332 enable_fd = open (trace_enable, O_RDWR);
335 clib_warning ("Couldn't open %s", trace_enable);
338 /* disable kernel tracing */
339 if (write (enable_fd, "0\n", 2) != 2)
341 clib_unix_warning ("disable tracing");
347 /* Read the trace data */
348 data_fd = open (trace_data, O_RDWR);
351 clib_warning ("Couldn't open %s", trace_data);
356 * Extract trace into a vector. Note that seq_printf() [kernel]
357 * is not guaranteed to produce 4096 bytes at a time.
359 vec_validate (data, 4095);
364 bytes = read (data_fd, data + pos, 4096);
368 total_bytes += bytes;
369 _vec_len (data) = total_bytes;
371 pos = vec_len (data);
372 vec_validate (data, vec_len (data) + 4095);
376 /* Synthesize events */
380 while ((evt = parse_sched_switch_trace (data, &index)))
384 fake_cpu_clock = evt->timestamp * em->cpu_timer.clocks_per_second;
386 ELOG_TYPE_DECLARE (e) =
388 .format = "%d: %s %s",.format_args = "i4T4t4",.n_enum_strings =
391 "running", "wakeup",}
395 u32 cpu, string_table_offset, which;
398 ed = elog_event_data_not_inline (em, &__ELOG_TYPE_VAR (e),
399 &em->default_track, fake_cpu_clock);
401 ed->string_table_offset = elog_id_for_pid (em, evt->task, evt->pid);
402 ed->which = evt->type;
404 _vec_len (evt->task) = 0;
410 * fd.io coding-style-patch-verification: ON
413 * eval: (c-set-style "gnu")