| #!/bin/sh |
| # SPDX-License-Identifier: GPL-2.0-only |
| |
| usage() { |
| echo "Dump boot-time tracing bootconfig from ftrace" |
| echo "Usage: $0 [--debug] [ > BOOTCONFIG-FILE]" |
| exit 1 |
| } |
| |
| DEBUG= |
| while [ x"$1" != x ]; do |
| case "$1" in |
| "--debug") |
| DEBUG=$1;; |
| -*) |
| usage |
| ;; |
| esac |
| shift 1 |
| done |
| |
| if [ x"$DEBUG" != x ]; then |
| set -x |
| fi |
| |
| TRACEFS=`grep -m 1 -w tracefs /proc/mounts | cut -f 2 -d " "` |
| if [ -z "$TRACEFS" ]; then |
| if ! grep -wq debugfs /proc/mounts; then |
| echo "Error: No tracefs/debugfs was mounted." |
| exit 1 |
| fi |
| TRACEFS=`grep -m 1 -w debugfs /proc/mounts | cut -f 2 -d " "`/tracing |
| if [ ! -d $TRACEFS ]; then |
| echo "Error: ftrace is not enabled on this kernel." 1>&2 |
| exit 1 |
| fi |
| fi |
| |
| ######## main ######### |
| |
| set -e |
| |
| emit_kv() { # key =|+= value |
| echo "$@" |
| } |
| |
| global_options() { |
| val=`cat $TRACEFS/max_graph_depth` |
| [ $val != 0 ] && emit_kv kernel.fgraph_max_depth = $val |
| if grep -qv "^#" $TRACEFS/set_graph_function $TRACEFS/set_graph_notrace ; then |
| cat 1>&2 << EOF |
| # WARN: kernel.fgraph_filters and kernel.fgraph_notrace are not supported, since the wild card expression was expanded and lost from memory. |
| EOF |
| fi |
| } |
| |
| kprobe_event_options() { |
| cat $TRACEFS/kprobe_events | while read p args; do |
| case $p in |
| r*) |
| cat 1>&2 << EOF |
| # WARN: A return probe found but it is not supported by bootconfig. Skip it. |
| EOF |
| continue;; |
| esac |
| p=${p#*:} |
| event=${p#*/} |
| group=${p%/*} |
| if [ $group != "kprobes" ]; then |
| cat 1>&2 << EOF |
| # WARN: kprobes group name $group is changed to "kprobes" for bootconfig. |
| EOF |
| fi |
| emit_kv $PREFIX.event.kprobes.$event.probes += $args |
| done |
| } |
| |
| synth_event_options() { |
| cat $TRACEFS/synthetic_events | while read event fields; do |
| emit_kv $PREFIX.event.synthetic.$event.fields = `echo $fields | sed "s/;/,/g"` |
| done |
| } |
| |
| # Variables resolver |
| DEFINED_VARS= |
| UNRESOLVED_EVENTS= |
| |
| defined_vars() { # event-dir |
| grep "^hist" $1/trigger | grep -o ':[a-zA-Z0-9]*=' |
| } |
| referred_vars() { |
| grep "^hist" $1/trigger | grep -o '$[a-zA-Z0-9]*' |
| } |
| |
| per_event_options() { # event-dir |
| evdir=$1 |
| # Check the special event which has no filter and no trigger |
| [ ! -f $evdir/filter ] && return |
| |
| if grep -q "^hist:" $evdir/trigger; then |
| # hist action can refer the undefined variables |
| __vars=`defined_vars $evdir` |
| for v in `referred_vars $evdir`; do |
| if echo $DEFINED_VARS $__vars | grep -vqw ${v#$}; then |
| # $v is not defined yet, defer it |
| UNRESOLVED_EVENTS="$UNRESOLVED_EVENTS $evdir" |
| return; |
| fi |
| done |
| DEFINED_VARS="$DEFINED_VARS "`defined_vars $evdir` |
| fi |
| grep -v "^#" $evdir/trigger | while read action active; do |
| emit_kv $PREFIX.event.$group.$event.actions += \'$action\' |
| done |
| |
| # enable is not checked; this is done by set_event in the instance. |
| val=`cat $evdir/filter` |
| if [ "$val" != "none" ]; then |
| emit_kv $PREFIX.event.$group.$event.filter = "$val" |
| fi |
| } |
| |
| retry_unresolved() { |
| unresolved=$UNRESOLVED_EVENTS |
| UNRESOLVED_EVENTS= |
| for evdir in $unresolved; do |
| event=${evdir##*/} |
| group=${evdir%/*}; group=${group##*/} |
| per_event_options $evdir |
| done |
| } |
| |
| event_options() { |
| # PREFIX and INSTANCE must be set |
| if [ $PREFIX = "ftrace" ]; then |
| # define the dynamic events |
| kprobe_event_options |
| synth_event_options |
| fi |
| for group in `ls $INSTANCE/events/` ; do |
| [ ! -d $INSTANCE/events/$group ] && continue |
| for event in `ls $INSTANCE/events/$group/` ;do |
| [ ! -d $INSTANCE/events/$group/$event ] && continue |
| per_event_options $INSTANCE/events/$group/$event |
| done |
| done |
| retry=0 |
| while [ $retry -lt 3 ]; do |
| retry_unresolved |
| retry=$((retry + 1)) |
| done |
| if [ "$UNRESOLVED_EVENTS" ]; then |
| cat 1>&2 << EOF |
| ! ERROR: hist triggers in $UNRESOLVED_EVENTS use some undefined variables. |
| EOF |
| fi |
| } |
| |
| is_default_trace_option() { # option |
| grep -qw $1 << EOF |
| print-parent |
| nosym-offset |
| nosym-addr |
| noverbose |
| noraw |
| nohex |
| nobin |
| noblock |
| trace_printk |
| annotate |
| nouserstacktrace |
| nosym-userobj |
| noprintk-msg-only |
| context-info |
| nolatency-format |
| record-cmd |
| norecord-tgid |
| overwrite |
| nodisable_on_free |
| irq-info |
| markers |
| noevent-fork |
| nopause-on-trace |
| function-trace |
| nofunction-fork |
| nodisplay-graph |
| nostacktrace |
| notest_nop_accept |
| notest_nop_refuse |
| EOF |
| } |
| |
| instance_options() { # [instance-name] |
| if [ $# -eq 0 ]; then |
| PREFIX="ftrace" |
| INSTANCE=$TRACEFS |
| else |
| PREFIX="ftrace.instance.$1" |
| INSTANCE=$TRACEFS/instances/$1 |
| fi |
| val= |
| for i in `cat $INSTANCE/trace_options`; do |
| is_default_trace_option $i && continue |
| val="$val, $i" |
| done |
| [ "$val" ] && emit_kv $PREFIX.options = "${val#,}" |
| val="local" |
| for i in `cat $INSTANCE/trace_clock` ; do |
| [ "${i#*]}" ] && continue |
| i=${i%]}; val=${i#[} |
| done |
| [ $val != "local" ] && emit_kv $PREFIX.trace_clock = $val |
| val=`cat $INSTANCE/buffer_size_kb` |
| if echo $val | grep -vq "expanded" ; then |
| emit_kv $PREFIX.buffer_size = $val"KB" |
| fi |
| if grep -q "is allocated" $INSTANCE/snapshot ; then |
| emit_kv $PREFIX.alloc_snapshot |
| fi |
| val=`cat $INSTANCE/tracing_cpumask` |
| if [ `echo $val | sed -e s/f//g`x != x ]; then |
| emit_kv $PREFIX.cpumask = $val |
| fi |
| |
| val= |
| for i in `cat $INSTANCE/set_event`; do |
| val="$val, $i" |
| done |
| [ "$val" ] && emit_kv $PREFIX.events = "${val#,}" |
| val=`cat $INSTANCE/current_tracer` |
| [ $val != nop ] && emit_kv $PREFIX.tracer = $val |
| if grep -qv "^#" $INSTANCE/set_ftrace_filter $INSTANCE/set_ftrace_notrace; then |
| cat 1>&2 << EOF |
| # WARN: kernel.ftrace.filters and kernel.ftrace.notrace are not supported, since the wild card expression was expanded and lost from memory. |
| EOF |
| fi |
| event_options |
| } |
| |
| global_options |
| instance_options |
| for i in `ls $TRACEFS/instances` ; do |
| instance_options $i |
| done |