RCU-7——RCU trace

基于msm-5.4

定义在 include/trace/events/rcu.h 中,需要使能 CONFIG_RCU_TRACE, 在 update.c 中生成trace函数文件。trace event有:

rcu_utilization
rcu_grace_period
rcu_future_grace_period,
rcu_grace_period_init,
rcu_exp_grace_period,
rcu_exp_funnel_lock,
rcu_nocb_wake,
rcu_preempt_task,
rcu_unlock_preempted_task,
rcu_quiescent_state_report,
rcu_fqs,
rcu_dyntick,
rcu_callback,
rcu_kfree_callback,
rcu_batch_start,
rcu_invoke_callback,
rcu_invoke_kfree_callback,
rcu_batch_end,
rcu_torture_read,
rcu_barrier,

 

一、各trace简介

1. rcu_utilization

用于利用率计算的开始/结束标记的跟踪点。按照惯例,该字符串采用以下格式:
“Start <activity>”——标记指定活动的开始,例如“上下文切换”。允许嵌套。
“End <activity>”——标记指定活动的结束。
“<activity>”中的“@”字符是注释字符:数据缩减脚本将忽略“@”及其行的其余部分。
kernel/rcu/tree_plugin.h 和 tree.c 中有大量调用。trace打印参数有:
"Start context switch"
"End context switch"
"Start boost kthread@init"
"Start boost kthread@rcu_wait"
"End boost kthread@rcu_wait"
"Start boost kthread@rcu_yield"
"End boost kthread@rcu_yield"
"End boost kthread@notreached"
"Start scheduler-tick"
"End scheduler-tick"
"Start RCU core"
"End RCU core"
"Start CPU kthread@rcu_wait"
"End CPU kthread@rcu_wait"
"Start CPU kthread@rcu_yield"
"End CPU kthread@rcu_yield"

打印内容:
TP_printk("%s", __entry->s)

调用路径:

    update_process_times //timer.c
        rcu_sched_clock_irq //tree.c
        rcu_core //tree.c
        rcu_note_context_switch //tree_plugin.h
        rcu_boost_kthread //tree_plugin.h
            trace_rcu_utilization


2. rcu_grace_period

宽限期事件的跟踪点。采用一个字符串来标识 RCU 类型、宽限期编号以及一个字符串来标识与宽限期相关的事件,如下所示:
“AccReadyCB”:CPU 将新的回调加速至 RCU_NEXT_READY_TAIL。
“AccWaitCB”:CPU 将新的回调加速至 RCU_WAIT_TAIL。
“newreq”:请求新的宽限期。
“start”:启动宽限期。
“cpustart”:CPU 首次注意到宽限期的开始。
“cpuqs”:CPU 进入静止状态。
“cpuonl”:CPU 上线。
“cpuofl”:CPU 下线。
“cpuofl-bgp”:CPU 在阻塞宽限期期间下线。
“reqwait”:GP kthread 处于休眠状态,等待宽限期请求。
"reqwaitsig": GP kthread 被 reqwait 状态的信号唤醒。
"fqswait": GP kthread 等待强制进入静止状态的时间。
"fqsstart": GP kthread 开始强制进入静止状态。
"fqsend": GP kthread 强制进入静止状态完成。
"fqswaitsig": GP kthread 被 fqswait 状态的信号唤醒。
"end": 结束宽限期。
"cpuend": CPU 首次注意到宽限期结束。

打印内容:
P_printk("%s %lu %s", __entry->rcuname, __entry->gp_seq, __entry->gpevent)

调用路径:

        rcu_accelerate_cbs_unlocked
        rcu_advance_cbs
    note_gp_changes
    rcu_gp_init
        __note_gp_changes
        rcu_gp_cleanup
        rcu_core
        __call_rcu_core
            rcu_accelerate_cbs //tree.c
                rcu_start_this_gp //tree.c
                rcu_accelerate_cbs //tree.c
                __note_gp_changes //tree.c
                rcu_gp_init //tree.c
                rcu_gp_fqs_loop //tree.c
                rcu_gp_cleanup //tree.c
                rcu_gp_kthread //tree.c
                rcutree_dying_cpu //tree.c
                rcutree_prepare_cpu //tree.c
                rcu_qs //tree_plugin.h
                    trace_rcu_grace_period


3. rcu_future_grace_period

用于未来宽限期事件的跟踪点。调用者应从 rcu_node 结构体中提取数据,rcuname 除外(来自 rcu_state 结构体)以及 event(以下之一):

“Startleaf”:根据叶节点数据请求宽限期。
“Prestarted”:有人抢先请求
“Startedleaf”:叶节点标记为未来 GP。
“Startedleafroot”:从叶节点到根节点的所有节点都标记为未来 GP。
“Startedroot”:根据根节点数据请求 nocb 宽限期。
“NoGPkthread”:RCU 宽限期 kthread 尚未启动。
“StartWait”:开始等待请求的宽限期。
“EndWait”:完成等待。
“Cleanup”:在上一次 GP 之后清理 rcu_node 结构体。
“CleanupMore”:已清理,需要另一个 GP。

打印内容:
TP_printk("%s %lu %lu %u %d %d %s", __entry->rcuname, __entry->gp_seq, __entry->gp_seq_req, __entry->level, __entry->grplo, __entry->grphi, __entry->gpevent)

调用路径:

    rcu_accelerate_cbs //tree.c
        rcu_start_this_gp
    rcu_gp_cleanup //tree.c
        rcu_future_gp_cleanup
rcu_spawn_gp_kthread //tree.c 创建的名为 rcu_state.name 的线程
    rcu_gp_kthread
        rcu_gp_cleanup
            trace_rcu_this_gp //tree.c
                trace_rcu_future_grace_period


4. rcu_grace_period_init

宽限期初始化事件的跟踪点。这些事件通过 RCU 类型、新的宽限期编号、rcu_node 结构层级、rcu_node 结构覆盖的起始和结束 CPU 以及将要等待的 CPU 掩码来区分。除 RCU 类型外,其他所有信息均从 rcu_node 结构中提取。

打印内容:
TP_printk("%s %lu %u %d %d %lx", __entry->rcuname, __entry->gp_seq, __entry->level, __entry->grplo, __entry->grphi, __entry->qsmask)

调用路径:

early_initcall(rcu_spawn_gp_kthread) //tree.c
    rcu_spawn_gp_kthread //tree.c
        kthread_create(rcu_gp_kthread, NULL, "%s", rcu_state.name) //创建这个内核线程
            rcu_gp_kthread //tree.c 需要增加宽限期时唤醒这个线程
                rcu_gp_init //tree.c
                    trace_rcu_grace_period_init(rcu_state.name, rnp...)


5. rcu_exp_grace_period

加急宽限期事件的跟踪点。采用标识 RCU 类型、加急宽限期序列号以及标识宽限期相关事件的字符串,如下所示:
“snap”:已捕获加急宽限期序列号的快照。
“start”:已启动实际加急宽限期。
“reset”:已开始重置树。
“select”:已开始选择要等待的 CPU。
“selectofl”:已选定 CPU 部分离线。
“startwait”:已开始等待选定的 CPU。
“end”:已结束实际加急宽限期。
“endwake”:已唤醒搭载者。
“done”:已由其他人为我们完成了加急宽限期。

打印内容:
TP_printk("%s %lu %s", __entry->rcuname, __entry->gpseq, __entry->gpevent)

调用路径:

            synchronize_rcu_expedited //tree_exp.h
                rcu_exp_gp_seq_snap //tree_exp.h
            exp_funnel_lock //tree_exp.h
            synchronize_rcu_expedited //tree_exp.h
                sync_exp_work_done //tree_exp.h
            synchronize_rcu_expedited
                exp_funnel_lock //tree_exp.h
            sync_rcu_exp_select_cpus
                sync_rcu_exp_select_node_cpus //tree_exp.h
                sync_rcu_exp_select_cpus //tree_exp.h
    wait_rcu_exp_gp //tree_exp.h
    synchronize_rcu_expedited //tree_exp.h
        rcu_exp_sel_wait_wake //tree_exp.h
            rcu_exp_wait_wake //tree_exp.h
                synchronize_sched_expedited_wait //tree_exp.h
                rcu_exp_wait_wake //tree_exp.h
                    trace_rcu_exp_gp_seq_snap


6. rcu_exp_funnel_lock

用于加速宽限期漏斗锁定事件的跟踪点。采用一个标识 RCU 类型的字符串、一个标识 rcu_node 组合树级别的整数、另一对标识与当前 rcu_node 结构关联的最低和最高编号 CPU 的整数,以及一个标识宽限期相关事件的字符串,如下所示:
“nxtlvl”:前进到 rcu_node 漏斗的下一级
“wait”:等待其他人执行加速 GP

打印内容:

TP_printk("%s %d %d %d %s", __entry->rcuname, __entry->level, __entry->grplo, __entry->grphi, __entry->gpevent)

调用路径:

    synchronize_rcu_expedited //tree_exp.h
        exp_funnel_lock //tree_exp.h
            trace_rcu_exp_funnel_lock


7. rcu_nocb_wake

RCU 无 CB CPU 回调切换的跟踪点。此事件旨在协助调试这些切换。
第一个参数是 RCU 类型名称,第二个参数是提取的卸载 CPU 数量。第三个也是最后一个参数是一个字符串,如下所示:

“WakeEmpty”:唤醒 rcuo kthread,将第一个 CB 清空列表。
“WakeEmptyIsDeferred”:稍后唤醒 rcuo kthread,将第一个 CB 清空列表。
“WakeOvf”:唤醒 rcuo kthread,CB 列表很大。
“WakeOvfIsDeferred”:稍后唤醒 rcuo kthread,CB 列表很大。
“WakeNot”:不唤醒 rcuo kthread。
“WakeNotPoll”:不唤醒 rcuo kthread,因为它正在轮询。
“DeferredWake”:执行“IsDeferred”唤醒。
"Poll": 开始 rcu_nocb_poll 的新轮询周期。
"Sleep": 休眠等待 !rcu_nocb_poll 的 GP。
"CBSleep": 休眠等待 !rcu_nocb_poll 的 CB。
"WokeEmpty": rcuo kthread 唤醒发现空列表。
"WokeNonEmpty": rcuo kthread 唤醒发现非空列表。
"WaitQueue": 入队操作部分完成,定时等待其完成。
"WokeQueue": 部分入队操作现已完成。

打印内容:

TP_printk("%s %d %s", __entry->rcuname, __entry->cpu, __entry->reason)

调用路径:

    wake_nocb_gp
    wake_nocb_gp_defer
    rcu_nocb_try_bypass
    __call_rcu_nocb_wake
    do_nocb_bypass_wakeup_timer
    nocb_gp_wait
    nocb_cb_wait
    do_nocb_deferred_wakeup_common
        trace_rcu_nocb_wake


8. rcu_preempt_task

可抢占式 RCU 读端临界区内阻塞任务的跟踪点。跟踪 RCU 的类型(将来可能包含 SRCU)、任务阻塞的宽限期编号(当前或下一个)以及任务的 PID。

打印内容:
TP_printk("%s %lu %d", __entry->rcuname, __entry->gp_seq, __entry->pid)

调用路径:

    __schedule(preempt) //core.c
    rcu_virt_note_context_switch(false) //rcutree.h
        rcu_note_context_switch(preempt) //tree_plugin.h
            trace_rcu_preempt_task 


9. rcu_unlock_preempted_task

跟踪在给定可抢占式 RCU 读端临界区内阻塞的任务,并确定其退出临界区的时间。跟踪 RCU 的类型(将来可能包含 SRCU)以及任务的 PID。

打印内容:
TP_printk("%s %lu %d", __entry->rcuname, __entry->gp_seq, __entry->pid)

调用路径:

rcu_exp_handler //tree_exp.h
rcu_note_context_switch //tree_plugin.h
rcu_flavor_sched_clock_irq //tree_plugin.h
exit_rcu //tree_plugin.h
    rcu_preempt_deferred_qs
    rcu_read_unlock_special
        rcu_preempt_deferred_qs_irqrestore //tree_plugin.h
    rcu_gp_init //tree.c
        rcu_preempt_check_blocked_tasks
            trace_rcu_unlock_preempted_task


10. rcu_quiescent_state_report

用于报告静止状态事件的跟踪点。这些跟踪点通过 RCU 类型、宽限期编号、静止低级实体的掩码、rcu_node 结构级别、rcu_node 结构覆盖的起始和结束 CPU,以及是否有任何阻塞任务阻塞当前宽限期来区分。除 RCU 类型外,其他所有信息均从 rcu_node 结构中提取。

打印内容:
TP_printk("%s %lu %d", __entry->rcuname, __entry->gp_seq, __entry->pid)

调用路径:

    rcu_gp_init //tree.c
    rcu_report_unblock_qs_rnp //tree.c
    rcu_report_qs_rdp //tree.c
    force_qs_rnp //tree.c
    rcu_cpu_starting //tree.c
    rcu_report_dead //tree.c
        rcu_report_qs_rnp //tree.c
        rcu_preempt_deferred_qs_irqrestore //tree_plugin.h
            trace_rcu_quiescent_state_report


11. rcu_fqs

force_quiescent_state() 检测到的静止状态的跟踪点。这些跟踪事件包括 RCU 的类型、被 CPU 阻止的宽限期编号、CPU 本身以及静止状态的类型。静止状态可以是“dti”(表示 dyntick-idle 模式)或“kick”(表示踢出处于 dyntick-idle 模式时间过长的 CPU)。

打印内容:
TP_printk("%s %lu %d %s", __entry->rcuname, __entry->gp_seq, __entry->cpu, __entry->qsevent)

调用路径:

rcu_gp_kthread //tree.c 内核线程
    rcu_gp_fqs_loop //tree.c
        rcu_gp_fqs
            dyntick_save_progress_counter //tree.c
        rcu_gp_fqs
            rcu_implicit_dynticks_qs //tree.c
                trace_rcu_fqs


12. rcu_dyntick

dyntick-idle 进入/退出事件的跟踪点。这些事件以字符串作为参数:“Start”表示进入 dyntick-idle 模式,“Startirq”表示从 irq/NMI 进入,“End”表示退出,“Endirq”表示退出 irq/NMI,“--=”表示事件进入空闲模式,“++=”表示事件离开空闲模式。
这些事件还接受一对数字,分别表示相关事件前后的嵌套深度,以及第三个参数是 ->dynticks 计数器。请注意,任务相关事件和中断相关事件使用两个独立的计数器,irq/NMI 的“++=”和“--=”事件会将计数器的值改变 2,否则会改变 1。

打印内容:
TP_printk("%s %lx %lx %#3x", __entry->polarity, __entry->oldnesting, __entry->newnesting, __entry->dynticks & 0xfff)

调用路径:

    rcu_idle_enter //tree.c 传false
    rcu_user_enter //tree.c 传true
        rcu_eqs_enter //tree.c
    rcu_nmi_exit //tree.c
    rcu_irq_exit //tree.c
        rcu_nmi_exit_common //tree.c
    rcu_idle_exit //tree.c
    rcu_user_exit
        rcu_eqs_exit //tree.c
    rcu_nmi_enter //tree.c
    rcu_irq_enter //tree.c
        rcu_nmi_enter_common //tree.c
            trace_rcu_dyntick


13. rcu_callback

注册单个 RCU 回调函数的跟踪点。第一个参数是 RCU 的类型,第二个参数是指向 RCU 回调本身的指针,第三个元素是排队的惰性回调数量,第四个元素是排队的回调总数。

打印内容:
TP_printk("%s rhp=%p func=%ps %ld/%ld", __entry->rcuname, __entry->rhp, __entry->func, __entry->qlen_lazy, __entry->qlen)

调用路径:

    call_rcu //tree.c
    kfree_call_rcu //tree.c
        __call_rcu //tree.c
            trace_rcu_callback


14. rcu_kfree_callback

用于注册特殊 kfree() 形式的单个 RCU 回调的跟踪点。第一个参数是 RCU 类型,第二个参数是指向 RCU 回调的指针,第三个参数是回调在封闭的 RCU 保护数据结构中的偏移量,第四个参数是排队的惰性回调数量,第五个参数是排队的回调总数。

打印内容:
TP_printk("%s rhp=%p func=%ld %ld/%ld", __entry->rcuname, __entry->rhp, __entry->offset, __entry->qlen_lazy, __entry->qlen)

调用路径: 没有调用路径


15. rcu_batch_start

用于标记 rcu_do_batch 开始的跟踪点,用于启动 RCU 回调调用。第一个参数是 RCU 类型,第二个参数是排队的惰性回调数量,第三个参数是排队的回调总数,第四个参数是当前 RCU 回调批次限制。

打印内容:
TP_printk("%s CBs=%ld/%ld bl=%ld", __entry->rcuname, __entry->qlen_lazy, __entry->qlen, __entry->blimit)

调用路径:

rcu_init //tree.c
    open_softirq(RCU_SOFTIRQ, rcu_core_si)
        rcu_core_si
rcu_spawn_core_kthreads //tree.c
    smp_hotplug_thread rcu_cpu_thread_spec //是每cpu一个的 rcuc/X 线程
        rcu_cpu_kthread
            rcu_core
                rcu_do_batch //tree.c
                    trace_rcu_batch_start


16. rcu_invoke_callback

单个 RCU 回调函数调用的跟踪点。第一个参数是 RCU 的类型,第二个参数是指向 RCU 回调函数本身的指针。

打印内容:
TP_printk("%s rhp=%p func=%ps", __entry->rcuname, __entry->rhp, __entry->func)

调用路径:

    rcu_do_batch //tree.c
        __rcu_reclaim //rcu.h
            trace_rcu_invoke_callback


17. rcu_invoke_kfree_callback

用于调用特殊 kfree() 形式的单个 RCU 回调的跟踪点。第一个参数表示 RCU 类型,第二个参数是指向 RCU 回调的指针,第三个参数是回调在封闭的受 RCU 保护的数据结构中的偏移量。

打印内容:
TP_printk("%s rhp=%p func=%ld", __entry->rcuname, __entry->rhp, __entry->offset)

调用路径:

    rcu_do_batch //tree.c
        __rcu_reclaim
            trace_rcu_invoke_kfree_callback


18. rcu_batch_end

在调用 RCU 回调后退出 rcu_do_batch 的跟踪点。第一个参数是 RCU 类型名称,第二个参数是实际调用的回调数量,第三个参数 (cb) 表示此批次开始时已准备好调用的回调是否仍在排队中,第四个参数 (nr) 是 need_resched() 的返回值,第五个参数 (iit) 表示当前任务为空闲任务,则为 1,第六个参数 (risk) 是 rcu_is_callbacks_kthread() 的返回值。

打印内容:
TP_printk("%s CBs-invoked=%d idle=%c%c%c%c", __entry->rcuname, __entry->callbacks_invoked,
__entry->cb ? 'C' : '.',
__entry->nr ? 'S' : '.',
__entry->iit ? 'I' : '.',
__entry->risk ? 'R' : '.')

调用路径:

    rcu_do_batch //tree.c
        trace_rcu_batch_end


19. rcu_torture_read

rcutorture 读取器的跟踪点。第一个参数是从 rcutorture 的角度来看的 RCU 类型名称,第二个参数是回调地址。第三个参数是开始时间(以秒为单位),最后两个参数分别是读取开始和结束时的宽限期数字。注意,回调地址可以为 NULL。

打印内容:
TP_printk("%s torture read %p %luus c: %lu %lu",__entry->rcutorturename, __entry->rhp, __entry->secs, __entry->c_old, __entry->c)

调用路径:

    rcu_ops.read_delay //rcutorture.c
    rcu_busted_ops.read_delay //rcutorture.c
    busted_srcud_ops.read_delay //rcutorture.c
    tasks_ops.read_delay //rcutorture.c
    trivial_ops.read_delay //rcutorture.c
srcu_ops.read_delay //rcutorture.c
srcud_ops.read_delay //rcutorture.c
    srcu_read_delay //rcutorture.c
        rcu_read_delay //rcutorture.c
        rcu_torture_one_read //rcutorture.c
            do_trace_rcu_torture_read //update.c
                trace_rcu_torture_read


20. rcu_barrier

rcu_barrier() 执行的跟踪点。字符串“s”描述了 rcu_barrier 的执行阶段:
“Begin”:rcu_barrier() 已启动。
“EarlyExit”:rcu_barrier() 被搭载,因此提前退出。
“Inc1”:rcu_barrier() 搭载检查计数器已递增。
“OfflineNoCB”:rcu_barrier() 在永不在线的 CPU 上发现回调函数。
“OnlineNoCB”:rcu_barrier() 发现在线且无 CB 的 CPU。
“OnlineQ”:rcu_barrier() 发现在线且有回调函数的 CPU。
“OnlineNQ”:rcu_barrier() 发现在线 CPU,但没有回调函数。
“IRQ”:rcu_barrier_callback() 回调函数已在远程 CPU 上发出。
“IRQNQ”:rcu_barrier_callback() 回调函数未发现回调函数。
“CB”:rcu_barrier_callback() 调用了一个回调,但不是最后一个。
“LastCB”:rcu_barrier_callback() 调用了最后一个回调。
“Inc2”:rcu_barrier() 捎带检查计数器已递增。
“cpu”参数表示 CPU,如果无意义则为 -1,“cnt”参数表示剩余回调的数量,“done”表示捎带计数。

打印内容:
TP_printk("%s %s cpu %d remaining %d # %lu", __entry->rcuname, __entry->s, __entry->cpu, __entry->cnt, __entry->done)

调用路径:

    rcu_barrier_callback
    rcu_barrier_func
    rcu_barrier
        rcu_barrier_trace //tree.c
            trace_rcu_barrier

 

===> 看起来实现逻辑有点像有限状态机。

 

posted on 2025-05-23 17:09  Hello-World3  阅读(74)  评论(0)    收藏  举报

导航