| 1 | |
| 2 | |
| 3 | * trace_boot.c
|
| 4 | * Tracing kernel boot-time
|
| 5 |
|
| 6 |
|
| 7 | #define pr_fmt(fmt) "trace_boot: " fmt
|
| 8 |
|
| 9 | #include <linux/bootconfig.h>
|
| 10 | #include <linux/cpumask.h>
|
| 11 | #include <linux/ftrace.h>
|
| 12 | #include <linux/init.h>
|
| 13 | #include <linux/kernel.h>
|
| 14 | #include <linux/mutex.h>
|
| 15 | #include <linux/string.h>
|
| 16 | #include <linux/slab.h>
|
| 17 | #include <linux/trace.h>
|
| 18 | #include <linux/trace_events.h>
|
| 19 |
|
| 20 | #include "trace.h"
|
| 21 | #include "trace_dynevent.h"
|
| 22 |
|
| 23 | #define MAX_BUF_LEN 256
|
| 24 |
|
| 25 | static void __init
|
| 26 | trace_boot_set_instance_options(struct trace_array *tr, struct xbc_node *node)
|
| 27 | {
|
| 28 | struct xbc_node *anode;
|
| 29 | const char *p;
|
| 30 | char buf[MAX_BUF_LEN];
|
| 31 | unsigned long v = 0;
|
| 32 |
|
| 33 |
|
| 34 | xbc_node_for_each_array_value(node, "options", anode, p) {
|
| 35 | if (strscpy(buf, p, ARRAY_SIZE(buf)) < 0) {
|
| 36 | pr_err("String is too long: %s\n", p);
|
| 37 | continue;
|
| 38 | }
|
| 39 |
|
| 40 | if (trace_set_options(tr, buf) < 0)
|
| 41 | pr_err("Failed to set option: %s\n", buf);
|
| 42 | }
|
| 43 |
|
| 44 | p = xbc_node_find_value(node, "tracing_on", NULL);
|
| 45 | if (p && *p != '\0') {
|
| 46 | if (kstrtoul(p, 10, &v))
|
| 47 | pr_err("Failed to set tracing on: %s\n", p);
|
| 48 | if (v)
|
| 49 | tracer_tracing_on(tr);
|
| 50 | else
|
| 51 | tracer_tracing_off(tr);
|
| 52 | }
|
| 53 |
|
| 54 | p = xbc_node_find_value(node, "trace_clock", NULL);
|
| 55 | if (p && *p != '\0') {
|
| 56 | if (tracing_set_clock(tr, p) < 0)
|
| 57 | pr_err("Failed to set trace clock: %s\n", p);
|
| 58 | }
|
| 59 |
|
| 60 | p = xbc_node_find_value(node, "buffer_size", NULL);
|
| 61 | if (p && *p != '\0') {
|
| 62 | v = memparse(p, NULL);
|
| 63 | if (v < PAGE_SIZE)
|
| 64 | pr_err("Buffer size is too small: %s\n", p);
|
| 65 | if (trace_array_is_readonly(tr) ||
|
| 66 | tracing_resize_ring_buffer(tr, v, RING_BUFFER_ALL_CPUS) < 0)
|
| 67 | pr_err("Failed to resize trace buffer to %s\n", p);
|
| 68 | }
|
| 69 |
|
| 70 | p = xbc_node_find_value(node, "cpumask", NULL);
|
| 71 | if (p && *p != '\0') {
|
| 72 | cpumask_var_t new_mask;
|
| 73 |
|
| 74 | if (alloc_cpumask_var(&new_mask, GFP_KERNEL)) {
|
| 75 | if (cpumask_parse(p, new_mask) < 0 ||
|
| 76 | tracing_set_cpumask(tr, new_mask) < 0)
|
| 77 | pr_err("Failed to set new CPU mask %s\n", p);
|
| 78 | free_cpumask_var(new_mask);
|
| 79 | }
|
| 80 | }
|
| 81 | }
|
| 82 |
|
| 83 | #ifdef CONFIG_EVENT_TRACING
|
| 84 | static void __init
|
| 85 | trace_boot_enable_events(struct trace_array *tr, struct xbc_node *node)
|
| 86 | {
|
| 87 | struct xbc_node *anode;
|
| 88 | char buf[MAX_BUF_LEN];
|
| 89 | const char *p;
|
| 90 |
|
| 91 | xbc_node_for_each_array_value(node, "events", anode, p) {
|
| 92 | if (strscpy(buf, p, ARRAY_SIZE(buf)) < 0) {
|
| 93 | pr_err("String is too long: %s\n", p);
|
| 94 | continue;
|
| 95 | }
|
| 96 |
|
| 97 | if (ftrace_set_clr_event(tr, buf, 1) < 0)
|
| 98 | pr_err("Failed to enable event: %s\n", p);
|
| 99 | }
|
| 100 | }
|
| 101 |
|
| 102 | #ifdef CONFIG_KPROBE_EVENTS
|
| 103 | static int __init
|
| 104 | trace_boot_add_kprobe_event(struct xbc_node *node, const char *event)
|
| 105 | {
|
| 106 | struct dynevent_cmd cmd;
|
| 107 | struct xbc_node *anode;
|
| 108 | char buf[MAX_BUF_LEN];
|
| 109 | const char *val;
|
| 110 | int ret = 0;
|
| 111 |
|
| 112 | xbc_node_for_each_array_value(node, "probes", anode, val) {
|
| 113 | kprobe_event_cmd_init(&cmd, buf, MAX_BUF_LEN);
|
| 114 |
|
| 115 | ret = kprobe_event_gen_cmd_start(&cmd, event, val);
|
| 116 | if (ret) {
|
| 117 | pr_err("Failed to generate probe: %s\n", buf);
|
| 118 | break;
|
| 119 | }
|
| 120 |
|
| 121 | ret = kprobe_event_gen_cmd_end(&cmd);
|
| 122 | if (ret) {
|
| 123 | pr_err("Failed to add probe: %s\n", buf);
|
| 124 | break;
|
| 125 | }
|
| 126 | }
|
| 127 |
|
| 128 | return ret;
|
| 129 | }
|
| 130 | #else
|
| 131 | static inline int __init
|
| 132 | trace_boot_add_kprobe_event(struct xbc_node *node, const char *event)
|
| 133 | {
|
| 134 | pr_err("Kprobe event is not supported.\n");
|
| 135 | return -ENOTSUPP;
|
| 136 | }
|
| 137 | #endif
|
| 138 |
|
| 139 | #ifdef CONFIG_SYNTH_EVENTS
|
| 140 | static int __init
|
| 141 | trace_boot_add_synth_event(struct xbc_node *node, const char *event)
|
| 142 | {
|
| 143 | struct dynevent_cmd cmd;
|
| 144 | struct xbc_node *anode;
|
| 145 | char buf[MAX_BUF_LEN];
|
| 146 | const char *p;
|
| 147 | int ret;
|
| 148 |
|
| 149 | synth_event_cmd_init(&cmd, buf, MAX_BUF_LEN);
|
| 150 |
|
| 151 | ret = synth_event_gen_cmd_start(&cmd, event, NULL);
|
| 152 | if (ret)
|
| 153 | return ret;
|
| 154 |
|
| 155 | xbc_node_for_each_array_value(node, "fields", anode, p) {
|
| 156 | ret = synth_event_add_field_str(&cmd, p);
|
| 157 | if (ret)
|
| 158 | return ret;
|
| 159 | }
|
| 160 |
|
| 161 | ret = synth_event_gen_cmd_end(&cmd);
|
| 162 | if (ret < 0)
|
| 163 | pr_err("Failed to add synthetic event: %s\n", buf);
|
| 164 |
|
| 165 | return ret;
|
| 166 | }
|
| 167 | #else
|
| 168 | static inline int __init
|
| 169 | trace_boot_add_synth_event(struct xbc_node *node, const char *event)
|
| 170 | {
|
| 171 | pr_err("Synthetic event is not supported.\n");
|
| 172 | return -ENOTSUPP;
|
| 173 | }
|
| 174 | #endif
|
| 175 |
|
| 176 | #if defined(CONFIG_EPROBE_EVENTS) || defined(CONFIG_FPROBE_EVENTS)
|
| 177 | static int __init
|
| 178 | trace_boot_add_probe_event(struct xbc_node *node, const char *group,
|
| 179 | const char *event, char type, const char *type_name)
|
| 180 | {
|
| 181 | struct xbc_node *anode;
|
| 182 | char buf[MAX_BUF_LEN];
|
| 183 | const char *val;
|
| 184 | int ret = 0;
|
| 185 |
|
| 186 | xbc_node_for_each_array_value(node, "probes", anode, val) {
|
| 187 | if (val[0] == type && (val[1] == ':' || isspace(val[1]))) {
|
| 188 | ret = strscpy(buf, val, MAX_BUF_LEN);
|
| 189 | if (ret < 0) {
|
| 190 | pr_err("%s command is too long: %s\n", type_name, val);
|
| 191 | break;
|
| 192 | }
|
| 193 | } else {
|
| 194 | ret = snprintf(buf, MAX_BUF_LEN, "%c:%s/%s %s", type, group, event, val);
|
| 195 | if (ret >= MAX_BUF_LEN || ret < 0) {
|
| 196 | pr_err("%s command is too long: %c:%s/%s %s\n",
|
| 197 | type_name, type, group, event, val);
|
| 198 | ret = -E2BIG;
|
| 199 | break;
|
| 200 | }
|
| 201 | }
|
| 202 |
|
| 203 | ret = dyn_event_create(buf, NULL);
|
| 204 | if (ret) {
|
| 205 | pr_err("Failed to add %s: %s\n", type_name, buf);
|
| 206 | break;
|
| 207 | }
|
| 208 | }
|
| 209 |
|
| 210 | return ret;
|
| 211 | }
|
| 212 | #endif
|
| 213 |
|
| 214 | #ifdef CONFIG_EPROBE_EVENTS
|
| 215 | static inline int __init
|
| 216 | trace_boot_add_eprobe_event(struct xbc_node *node, const char *group,
|
| 217 | const char *event)
|
| 218 | {
|
| 219 | return trace_boot_add_probe_event(node, group, event, 'e', "eprobe");
|
| 220 | }
|
| 221 | #else
|
| 222 | static inline int __init
|
| 223 | trace_boot_add_eprobe_event(struct xbc_node *node, const char *group,
|
| 224 | const char *event)
|
| 225 | {
|
| 226 | pr_err("Event probe is not supported.\n");
|
| 227 | return -EOPNOTSUPP;
|
| 228 | }
|
| 229 | #endif
|
| 230 |
|
| 231 | #ifdef CONFIG_FPROBE_EVENTS
|
| 232 | static inline int __init
|
| 233 | trace_boot_add_fprobe_event(struct xbc_node *node, const char *group,
|
| 234 | const char *event)
|
| 235 | {
|
| 236 | return trace_boot_add_probe_event(node, group, event, 'f', "fprobe");
|
| 237 | }
|
| 238 |
|
| 239 | static inline int __init
|
| 240 | trace_boot_add_tprobe_event(struct xbc_node *node, const char *group,
|
| 241 | const char *event)
|
| 242 | {
|
| 243 | return trace_boot_add_probe_event(node, group, event, 't', "tprobe");
|
| 244 | }
|
| 245 | #else
|
| 246 | static inline int __init
|
| 247 | trace_boot_add_fprobe_event(struct xbc_node *node, const char *group,
|
| 248 | const char *event)
|
| 249 | {
|
| 250 | pr_err("Fprobe event is not supported.\n");
|
| 251 | return -EOPNOTSUPP;
|
| 252 | }
|
| 253 |
|
| 254 | static inline int __init
|
| 255 | trace_boot_add_tprobe_event(struct xbc_node *node, const char *group,
|
| 256 | const char *event)
|
| 257 | {
|
| 258 | pr_err("Tracepoint probe is not supported.\n");
|
| 259 | return -EOPNOTSUPP;
|
| 260 | }
|
| 261 | #endif
|
| 262 |
|
| 263 | #ifdef CONFIG_HIST_TRIGGERS
|
| 264 | static int __init __printf(3, 4)
|
| 265 | append_printf(char **bufp, char *end, const char *fmt, ...)
|
| 266 | {
|
| 267 | va_list args;
|
| 268 | int ret;
|
| 269 |
|
| 270 | if (*bufp == end)
|
| 271 | return -ENOSPC;
|
| 272 |
|
| 273 | va_start(args, fmt);
|
| 274 | ret = vsnprintf(*bufp, end - *bufp, fmt, args);
|
| 275 | if (ret < end - *bufp) {
|
| 276 | *bufp += ret;
|
| 277 | } else {
|
| 278 | *bufp = end;
|
| 279 | ret = -ERANGE;
|
| 280 | }
|
| 281 | va_end(args);
|
| 282 |
|
| 283 | return ret;
|
| 284 | }
|
| 285 |
|
| 286 | static int __init
|
| 287 | append_str_nospace(char **bufp, char *end, const char *str)
|
| 288 | {
|
| 289 | char *p = *bufp;
|
| 290 | int len;
|
| 291 |
|
| 292 | while (p < end - 1 && *str != '\0') {
|
| 293 | if (!isspace(*str))
|
| 294 | *(p++) = *str;
|
| 295 | str++;
|
| 296 | }
|
| 297 | *p = '\0';
|
| 298 | if (p == end - 1) {
|
| 299 | *bufp = end;
|
| 300 | return -ENOSPC;
|
| 301 | }
|
| 302 | len = p - *bufp;
|
| 303 | *bufp = p;
|
| 304 | return (int)len;
|
| 305 | }
|
| 306 |
|
| 307 | static int __init
|
| 308 | trace_boot_hist_add_array(struct xbc_node *hnode, char **bufp,
|
| 309 | char *end, const char *key)
|
| 310 | {
|
| 311 | struct xbc_node *anode;
|
| 312 | const char *p;
|
| 313 | char sep;
|
| 314 |
|
| 315 | p = xbc_node_find_value(hnode, key, &anode);
|
| 316 | if (p) {
|
| 317 | if (!anode) {
|
| 318 | pr_err("hist.%s requires value(s).\n", key);
|
| 319 | return -EINVAL;
|
| 320 | }
|
| 321 |
|
| 322 | append_printf(bufp, end, ":%s", key);
|
| 323 | sep = '=';
|
| 324 | xbc_array_for_each_value(anode, p) {
|
| 325 | append_printf(bufp, end, "%c%s", sep, p);
|
| 326 | if (sep == '=')
|
| 327 | sep = ',';
|
| 328 | }
|
| 329 | } else
|
| 330 | return -ENOENT;
|
| 331 |
|
| 332 | return 0;
|
| 333 | }
|
| 334 |
|
| 335 | static int __init
|
| 336 | trace_boot_hist_add_one_handler(struct xbc_node *hnode, char **bufp,
|
| 337 | char *end, const char *handler,
|
| 338 | const char *param)
|
| 339 | {
|
| 340 | struct xbc_node *knode, *anode;
|
| 341 | const char *p;
|
| 342 | char sep;
|
| 343 |
|
| 344 |
|
| 345 | p = xbc_node_find_value(hnode, param, NULL);
|
| 346 | if (!p) {
|
| 347 | pr_err("hist.%s requires '%s' option.\n",
|
| 348 | xbc_node_get_data(hnode), param);
|
| 349 | return -EINVAL;
|
| 350 | }
|
| 351 | append_printf(bufp, end, ":%s(%s)", handler, p);
|
| 352 |
|
| 353 |
|
| 354 | knode = xbc_node_find_subkey(hnode, "trace");
|
| 355 | if (!knode)
|
| 356 | knode = xbc_node_find_subkey(hnode, "save");
|
| 357 |
|
| 358 | if (knode) {
|
| 359 | anode = xbc_node_get_child(knode);
|
| 360 | if (!anode || !xbc_node_is_value(anode)) {
|
| 361 | pr_err("hist.%s.%s requires value(s).\n",
|
| 362 | xbc_node_get_data(hnode),
|
| 363 | xbc_node_get_data(knode));
|
| 364 | return -EINVAL;
|
| 365 | }
|
| 366 |
|
| 367 | append_printf(bufp, end, ".%s", xbc_node_get_data(knode));
|
| 368 | sep = '(';
|
| 369 | xbc_array_for_each_value(anode, p) {
|
| 370 | append_printf(bufp, end, "%c%s", sep, p);
|
| 371 | if (sep == '(')
|
| 372 | sep = ',';
|
| 373 | }
|
| 374 | append_printf(bufp, end, ")");
|
| 375 | } else if (xbc_node_find_subkey(hnode, "snapshot")) {
|
| 376 | append_printf(bufp, end, ".snapshot()");
|
| 377 | } else {
|
| 378 | pr_err("hist.%s requires an action.\n",
|
| 379 | xbc_node_get_data(hnode));
|
| 380 | return -EINVAL;
|
| 381 | }
|
| 382 |
|
| 383 | return 0;
|
| 384 | }
|
| 385 |
|
| 386 | static int __init
|
| 387 | trace_boot_hist_add_handlers(struct xbc_node *hnode, char **bufp,
|
| 388 | char *end, const char *param)
|
| 389 | {
|
| 390 | struct xbc_node *node;
|
| 391 | const char *p, *handler;
|
| 392 | int ret = 0;
|
| 393 |
|
| 394 | handler = xbc_node_get_data(hnode);
|
| 395 |
|
| 396 | xbc_node_for_each_subkey(hnode, node) {
|
| 397 | p = xbc_node_get_data(node);
|
| 398 | if (!isdigit(p[0]))
|
| 399 | continue;
|
| 400 |
|
| 401 | ret = trace_boot_hist_add_one_handler(node, bufp, end, handler, param);
|
| 402 | if (ret < 0)
|
| 403 | break;
|
| 404 | }
|
| 405 |
|
| 406 | if (xbc_node_find_subkey(hnode, param))
|
| 407 | ret = trace_boot_hist_add_one_handler(hnode, bufp, end, handler, param);
|
| 408 |
|
| 409 | return ret;
|
| 410 | }
|
| 411 |
|
| 412 | |
| 413 | * Histogram boottime tracing syntax.
|
| 414 | *
|
| 415 | * ftrace.[instance.INSTANCE.]event.GROUP.EVENT.hist[.N] {
|
| 416 | * keys = <KEY>[,...]
|
| 417 | * values = <VAL>[,...]
|
| 418 | * sort = <SORT-KEY>[,...]
|
| 419 | * size = <ENTRIES>
|
| 420 | * name = <HISTNAME>
|
| 421 | * var { <VAR> = <EXPR> ... }
|
| 422 | * pause|continue|clear
|
| 423 | * onmax|onchange[.N] { var = <VAR>; <ACTION> [= <PARAM>] }
|
| 424 | * onmatch[.N] { event = <EVENT>; <ACTION> [= <PARAM>] }
|
| 425 | * filter = <FILTER>
|
| 426 | * }
|
| 427 | *
|
| 428 | * Where <ACTION> are;
|
| 429 | *
|
| 430 | * trace = <EVENT>, <ARG1>[, ...]
|
| 431 | * save = <ARG1>[, ...]
|
| 432 | * snapshot
|
| 433 |
|
| 434 | static int __init
|
| 435 | trace_boot_compose_hist_cmd(struct xbc_node *hnode, char *buf, size_t size)
|
| 436 | {
|
| 437 | struct xbc_node *node, *knode;
|
| 438 | char *end = buf + size;
|
| 439 | const char *p;
|
| 440 | int ret = 0;
|
| 441 |
|
| 442 | append_printf(&buf, end, "hist");
|
| 443 |
|
| 444 | ret = trace_boot_hist_add_array(hnode, &buf, end, "keys");
|
| 445 | if (ret < 0) {
|
| 446 | if (ret == -ENOENT)
|
| 447 | pr_err("hist requires keys.\n");
|
| 448 | return -EINVAL;
|
| 449 | }
|
| 450 |
|
| 451 | ret = trace_boot_hist_add_array(hnode, &buf, end, "values");
|
| 452 | if (ret == -EINVAL)
|
| 453 | return ret;
|
| 454 | ret = trace_boot_hist_add_array(hnode, &buf, end, "sort");
|
| 455 | if (ret == -EINVAL)
|
| 456 | return ret;
|
| 457 |
|
| 458 | p = xbc_node_find_value(hnode, "size", NULL);
|
| 459 | if (p)
|
| 460 | append_printf(&buf, end, ":size=%s", p);
|
| 461 |
|
| 462 | p = xbc_node_find_value(hnode, "name", NULL);
|
| 463 | if (p)
|
| 464 | append_printf(&buf, end, ":name=%s", p);
|
| 465 |
|
| 466 | node = xbc_node_find_subkey(hnode, "var");
|
| 467 | if (node) {
|
| 468 | xbc_node_for_each_key_value(node, knode, p) {
|
| 469 |
|
| 470 | append_printf(&buf, end, ":%s=",
|
| 471 | xbc_node_get_data(knode));
|
| 472 | append_str_nospace(&buf, end, p);
|
| 473 | }
|
| 474 | }
|
| 475 |
|
| 476 |
|
| 477 | if (xbc_node_find_value(hnode, "pause", NULL))
|
| 478 | append_printf(&buf, end, ":pause");
|
| 479 | else if (xbc_node_find_value(hnode, "continue", NULL))
|
| 480 | append_printf(&buf, end, ":continue");
|
| 481 | else if (xbc_node_find_value(hnode, "clear", NULL))
|
| 482 | append_printf(&buf, end, ":clear");
|
| 483 |
|
| 484 |
|
| 485 | node = xbc_node_find_subkey(hnode, "onmax");
|
| 486 | if (node && trace_boot_hist_add_handlers(node, &buf, end, "var") < 0)
|
| 487 | return -EINVAL;
|
| 488 | node = xbc_node_find_subkey(hnode, "onchange");
|
| 489 | if (node && trace_boot_hist_add_handlers(node, &buf, end, "var") < 0)
|
| 490 | return -EINVAL;
|
| 491 | node = xbc_node_find_subkey(hnode, "onmatch");
|
| 492 | if (node && trace_boot_hist_add_handlers(node, &buf, end, "event") < 0)
|
| 493 | return -EINVAL;
|
| 494 |
|
| 495 | p = xbc_node_find_value(hnode, "filter", NULL);
|
| 496 | if (p)
|
| 497 | append_printf(&buf, end, " if %s", p);
|
| 498 |
|
| 499 | if (buf == end) {
|
| 500 | pr_err("hist exceeds the max command length.\n");
|
| 501 | return -E2BIG;
|
| 502 | }
|
| 503 |
|
| 504 | return 0;
|
| 505 | }
|
| 506 |
|
| 507 | static void __init
|
| 508 | trace_boot_init_histograms(struct trace_event_file *file,
|
| 509 | struct xbc_node *hnode, char *buf, size_t size)
|
| 510 | {
|
| 511 | struct xbc_node *node;
|
| 512 | const char *p;
|
| 513 | char *tmp;
|
| 514 |
|
| 515 | xbc_node_for_each_subkey(hnode, node) {
|
| 516 | p = xbc_node_get_data(node);
|
| 517 | if (!isdigit(p[0]))
|
| 518 | continue;
|
| 519 |
|
| 520 | if (trace_boot_compose_hist_cmd(node, buf, size) == 0) {
|
| 521 | tmp = kstrdup(buf, GFP_KERNEL);
|
| 522 | if (!tmp)
|
| 523 | return;
|
| 524 | if (trigger_process_regex(file, buf) < 0)
|
| 525 | pr_err("Failed to apply hist trigger: %s\n", tmp);
|
| 526 | kfree(tmp);
|
| 527 | }
|
| 528 | }
|
| 529 |
|
| 530 | if (xbc_node_find_subkey(hnode, "keys")) {
|
| 531 | if (trace_boot_compose_hist_cmd(hnode, buf, size) == 0) {
|
| 532 | tmp = kstrdup(buf, GFP_KERNEL);
|
| 533 | if (!tmp)
|
| 534 | return;
|
| 535 | if (trigger_process_regex(file, buf) < 0)
|
| 536 | pr_err("Failed to apply hist trigger: %s\n", tmp);
|
| 537 | kfree(tmp);
|
| 538 | }
|
| 539 | }
|
| 540 | }
|
| 541 | #else
|
| 542 | static void __init
|
| 543 | trace_boot_init_histograms(struct trace_event_file *file,
|
| 544 | struct xbc_node *hnode, char *buf, size_t size)
|
| 545 | {
|
| 546 |
|
| 547 | }
|
| 548 | #endif
|
| 549 |
|
| 550 | static void __init
|
| 551 | trace_boot_init_one_event(struct trace_array *tr, struct xbc_node *gnode,
|
| 552 | struct xbc_node *enode)
|
| 553 | {
|
| 554 | struct trace_event_file *file;
|
| 555 | struct xbc_node *anode;
|
| 556 | char buf[MAX_BUF_LEN];
|
| 557 | const char *p, *group, *event;
|
| 558 |
|
| 559 | group = xbc_node_get_data(gnode);
|
| 560 | event = xbc_node_get_data(enode);
|
| 561 |
|
| 562 | if (!strcmp(group, "kprobes"))
|
| 563 | if (trace_boot_add_kprobe_event(enode, event) < 0)
|
| 564 | return;
|
| 565 | if (!strcmp(group, "synthetic"))
|
| 566 | if (trace_boot_add_synth_event(enode, event) < 0)
|
| 567 | return;
|
| 568 | if (!strcmp(group, "eprobes"))
|
| 569 | if (trace_boot_add_eprobe_event(enode, group, event) < 0)
|
| 570 | return;
|
| 571 | if (!strcmp(group, "fprobes"))
|
| 572 | if (trace_boot_add_fprobe_event(enode, group, event) < 0)
|
| 573 | return;
|
| 574 | if (!strcmp(group, "tracepoints") || !strcmp(group, "tprobes"))
|
| 575 | if (trace_boot_add_tprobe_event(enode, group, event) < 0)
|
| 576 | return;
|
| 577 |
|
| 578 | mutex_lock(&event_mutex);
|
| 579 | file = find_event_file(tr, group, event);
|
| 580 | if (!file) {
|
| 581 | pr_err("Failed to find event: %s:%s\n", group, event);
|
| 582 | goto out;
|
| 583 | }
|
| 584 |
|
| 585 | p = xbc_node_find_value(enode, "filter", NULL);
|
| 586 | if (p && *p != '\0') {
|
| 587 | if (strscpy(buf, p, ARRAY_SIZE(buf)) < 0)
|
| 588 | pr_err("filter string is too long: %s\n", p);
|
| 589 | else if (apply_event_filter(file, buf) < 0)
|
| 590 | pr_err("Failed to apply filter: %s\n", buf);
|
| 591 | }
|
| 592 |
|
| 593 | if (IS_ENABLED(CONFIG_HIST_TRIGGERS)) {
|
| 594 | xbc_node_for_each_array_value(enode, "actions", anode, p) {
|
| 595 | if (strscpy(buf, p, ARRAY_SIZE(buf)) < 0)
|
| 596 | pr_err("action string is too long: %s\n", p);
|
| 597 | else if (trigger_process_regex(file, buf) < 0)
|
| 598 | pr_err("Failed to apply an action: %s\n", p);
|
| 599 | }
|
| 600 | anode = xbc_node_find_subkey(enode, "hist");
|
| 601 | if (anode)
|
| 602 | trace_boot_init_histograms(file, anode, buf, ARRAY_SIZE(buf));
|
| 603 | } else if (xbc_node_find_value(enode, "actions", NULL))
|
| 604 | pr_err("Failed to apply event actions because CONFIG_HIST_TRIGGERS is not set.\n");
|
| 605 |
|
| 606 | if (xbc_node_find_value(enode, "enable", NULL)) {
|
| 607 | if (trace_event_enable_disable(file, 1, 0) < 0)
|
| 608 | pr_err("Failed to enable event node: %s:%s\n",
|
| 609 | group, event);
|
| 610 | }
|
| 611 | out:
|
| 612 | mutex_unlock(&event_mutex);
|
| 613 | }
|
| 614 |
|
| 615 | static void __init
|
| 616 | trace_boot_init_events(struct trace_array *tr, struct xbc_node *node)
|
| 617 | {
|
| 618 | struct xbc_node *gnode, *enode;
|
| 619 | bool enable, enable_all = false;
|
| 620 | const char *data;
|
| 621 |
|
| 622 | node = xbc_node_find_subkey(node, "event");
|
| 623 | if (!node)
|
| 624 | return;
|
| 625 |
|
| 626 | xbc_node_for_each_subkey(node, gnode) {
|
| 627 | data = xbc_node_get_data(gnode);
|
| 628 | if (!strcmp(data, "enable")) {
|
| 629 | enable_all = true;
|
| 630 | continue;
|
| 631 | }
|
| 632 | enable = false;
|
| 633 | xbc_node_for_each_subkey(gnode, enode) {
|
| 634 | data = xbc_node_get_data(enode);
|
| 635 | if (!strcmp(data, "enable")) {
|
| 636 | enable = true;
|
| 637 | continue;
|
| 638 | }
|
| 639 | trace_boot_init_one_event(tr, gnode, enode);
|
| 640 | }
|
| 641 |
|
| 642 | if (enable) {
|
| 643 | data = xbc_node_get_data(gnode);
|
| 644 | trace_array_set_clr_event(tr, data, NULL, true);
|
| 645 | }
|
| 646 | }
|
| 647 |
|
| 648 | if (enable_all)
|
| 649 | trace_array_set_clr_event(tr, NULL, NULL, true);
|
| 650 | }
|
| 651 | #else
|
| 652 | #define trace_boot_enable_events(tr, node) do {} while (0)
|
| 653 | #define trace_boot_init_events(tr, node) do {} while (0)
|
| 654 | #endif
|
| 655 |
|
| 656 | #ifdef CONFIG_DYNAMIC_FTRACE
|
| 657 | static void __init
|
| 658 | trace_boot_set_ftrace_filter(struct trace_array *tr, struct xbc_node *node)
|
| 659 | {
|
| 660 | struct xbc_node *anode;
|
| 661 | const char *p;
|
| 662 | char *q;
|
| 663 |
|
| 664 | xbc_node_for_each_array_value(node, "ftrace.filters", anode, p) {
|
| 665 | q = kstrdup(p, GFP_KERNEL);
|
| 666 | if (!q)
|
| 667 | return;
|
| 668 | if (ftrace_set_filter(tr->ops, q, strlen(q), 0) < 0)
|
| 669 | pr_err("Failed to add %s to ftrace filter\n", p);
|
| 670 | else
|
| 671 | ftrace_filter_param = true;
|
| 672 | kfree(q);
|
| 673 | }
|
| 674 | xbc_node_for_each_array_value(node, "ftrace.notraces", anode, p) {
|
| 675 | q = kstrdup(p, GFP_KERNEL);
|
| 676 | if (!q)
|
| 677 | return;
|
| 678 | if (ftrace_set_notrace(tr->ops, q, strlen(q), 0) < 0)
|
| 679 | pr_err("Failed to add %s to ftrace filter\n", p);
|
| 680 | else
|
| 681 | ftrace_filter_param = true;
|
| 682 | kfree(q);
|
| 683 | }
|
| 684 | }
|
| 685 | #else
|
| 686 | #define trace_boot_set_ftrace_filter(tr, node) do {} while (0)
|
| 687 | #endif
|
| 688 |
|
| 689 | static void __init
|
| 690 | trace_boot_enable_tracer(struct trace_array *tr, struct xbc_node *node)
|
| 691 | {
|
| 692 | const char *p;
|
| 693 |
|
| 694 | trace_boot_set_ftrace_filter(tr, node);
|
| 695 |
|
| 696 | p = xbc_node_find_value(node, "tracer", NULL);
|
| 697 | if (p && *p != '\0') {
|
| 698 | if (trace_array_is_readonly(tr) || tracing_set_tracer(tr, p) < 0)
|
| 699 | pr_err("Failed to set given tracer: %s\n", p);
|
| 700 | }
|
| 701 |
|
| 702 |
|
| 703 | if (xbc_node_find_value(node, "alloc_snapshot", NULL)) {
|
| 704 | if (tracing_alloc_snapshot_instance(tr) < 0)
|
| 705 | pr_err("Failed to allocate snapshot buffer\n");
|
| 706 | }
|
| 707 | }
|
| 708 |
|
| 709 | static void __init
|
| 710 | trace_boot_init_one_instance(struct trace_array *tr, struct xbc_node *node)
|
| 711 | {
|
| 712 | trace_boot_set_instance_options(tr, node);
|
| 713 | trace_boot_init_events(tr, node);
|
| 714 | trace_boot_enable_events(tr, node);
|
| 715 | trace_boot_enable_tracer(tr, node);
|
| 716 | }
|
| 717 |
|
| 718 | static void __init
|
| 719 | trace_boot_init_instances(struct xbc_node *node)
|
| 720 | {
|
| 721 | struct xbc_node *inode;
|
| 722 | struct trace_array *tr;
|
| 723 | const char *p;
|
| 724 |
|
| 725 | node = xbc_node_find_subkey(node, "instance");
|
| 726 | if (!node)
|
| 727 | return;
|
| 728 |
|
| 729 | xbc_node_for_each_subkey(node, inode) {
|
| 730 | p = xbc_node_get_data(inode);
|
| 731 | if (!p || *p == '\0')
|
| 732 | continue;
|
| 733 |
|
| 734 | tr = trace_array_get_by_name(p, NULL);
|
| 735 | if (!tr) {
|
| 736 | pr_err("Failed to get trace instance %s\n", p);
|
| 737 | continue;
|
| 738 | }
|
| 739 | trace_boot_init_one_instance(tr, inode);
|
| 740 | trace_array_put(tr);
|
| 741 | }
|
| 742 | }
|
| 743 |
|
| 744 | static int __init trace_boot_init(void)
|
| 745 | {
|
| 746 | struct xbc_node *trace_node;
|
| 747 | struct trace_array *tr;
|
| 748 |
|
| 749 | trace_node = xbc_find_node("ftrace");
|
| 750 | if (!trace_node)
|
| 751 | return 0;
|
| 752 |
|
| 753 | tr = top_trace_array();
|
| 754 | if (!tr)
|
| 755 | return 0;
|
| 756 |
|
| 757 |
|
| 758 | trace_boot_init_one_instance(tr, trace_node);
|
| 759 | trace_boot_init_instances(trace_node);
|
| 760 |
|
| 761 | disable_tracing_selftest("running boot-time tracing");
|
| 762 |
|
| 763 | return 0;
|
| 764 | }
|
| 765 | |
| 766 | * Start tracing at the end of core-initcall, so that it starts tracing
|
| 767 | * from the beginning of postcore_initcall.
|
| 768 |
|
| 769 | core_initcall_sync(trace_boot_init);
|