内核的各个子系统已经有大量的跟踪点,如果这些跟踪点无法满足工作中的需求,可以自己手动添加跟踪点。 添加跟踪点有两种方式,一种是仿照events目录下的跟踪点,使用TRACEEVENT()宏添加。另一种是参考内核目录samplestraceevents添加。本文对这两种方式分别进行介绍。 使用TRACEEVENT定义tracepoint 我们仿照eventstimertimerstart,添加一个timerstat的跟踪点,获取startpid和slack参数。 首先,需要在includetraceeventstimer。h头文件种添加名为timerstat的跟踪点。 c timerstatftraceinterfacetimerstat timer:pointertostructtimerlist TRACEEVENT(timerstat, TPPROTO(structtimerlisttimer), TPARGS(timer), TPSTRUCTentry( field(void,timer) field(int,startpid) field(int,slack) ), TPfastassign( entrytimertimer; entrystartpidtimerstartpid; entryslacktimerslack; ), TPprintk(ftraceinterfacetimerstat:timerppiddslackd, entrytimer,entrystartpid,entryslack) ); TRACEEVENT()宏如下 c defineTRACEEVENT(name,proto,args,struct,assign,print) DEFINETRACE(name) name:表示跟踪点的名字,如上面的timerstat。 proto:表示跟踪点调用的入参的原型,比如timer类型为structtimerlist。 args:表示参数。 struct:定义跟踪器内部使用的entry数据结构。 assign:把参数复制到entry数据结构中。 print:定义输出的格式。 接着在kernelkerneltimetimer。cdebugactivate()添加tracetimerstat()。 c staticinlinevoid debugactivate(structtimerlisttimer,unsignedlongexpires) { debugtimeractivate(timer); tracetimerstart(timer,expires,timerflags); tracetimerstat(timer); } 重新编译内核后,烧写到设备中,即可看到sys节点已经有了新增的跟踪点。 !〔〕(http:linuxdriver。topBlog2023202301261736287。png) 使能跟踪点后,查看trace点的输出。 !〔〕(http:linuxdriver。topBlog2023202301261736280。png) 编译为独立的ko文件 内核还提供了一个跟踪点的例子,在samplestraceevents目录下。 traceeventinit()创建内核线程一个名为eventsample内核线程。 c staticintinittraceeventinit(void) { simpletskkthreadrun(simplethread,NULL,eventsample); if(ISERR(simpletsk)) return1; return0; } kthreadshouldstop()用于创建的线程检查结束标志,并决定是否退出。 c staticintsimplethread(voidarg) { intcnt0; while(!kthreadshouldstop()) simplethreadfunc(cnt); return0; } setcurrentstate()来设置进程的状态,设置为TASKINTERRUPTIBLE表示是可以被信号和wakeup()唤醒的,当信号到来时,进程会被设置为可运行。 scheduletimeout()将当前task调度出cpu,重新调度间隔为HZ。接着trace开头的函数就会依次打印跟踪点的信息。 c staticvoidsimplethreadfunc(intcnt) { intarray〔6〕; intlencnt5; inti; setcurrentstate(TASKINTERRUPTIBLE); scheduletimeout(HZ); for(i0;ilen;i) array〔i〕i1; array〔i〕0; Sillytracepoints tracefoobar(hello,cnt,array,randomstrings〔len〕, tskcpusallowed(current)); tracefoowithtemplatesimple(HELLO,cnt); tracefoobarwithcond(Sometimesprint,cnt); tracefoowithtemplatecond(printsothertimes,cnt); tracefoowithtemplateprint(Ihavetobedifferent,cnt); } tracefoowithtemplatesimple跟踪点的实现方式也是使用的TRACEEVENT()宏,这里不再赘述。 最后将文件编译为ko拷贝到设备上insmod后,即可看到sys目录下已经有新增的节点。 bash cdhomezhongyicoderk3399linuxreleasev2。5。120210301kernelsamplestraceevents makeChomezhongyicoderk3399linuxreleasev2。5。120210301kernelM(pwd)modules bash rootfirefly:syskerneldebugtracingcatavailableeventsgrepsample sampletrace:foobar sampletrace:foobarwithcond race:foobarwithfn sampletrace:foowithtemplatesimple sampletrace:foowithtemplatecond sampletrace:foowithtemplatefn sampletrace:foowithtemplateprint power:pstatesample bash rootfirefly:syskerneldebugtracingcdeventssampletrace rootfirefly:syskerneldebugtracingeventssampletracels enablefoobarwithcondfoowithtemplatefn filterfoobarwithfnfoowithtemplateprint foobarfoowithtemplatecondfoowithtemplsimple rootfirefly:syskerneldebugtracingeventssampletraceecho1enable rootfirefly:syskerneldebugtracingeventssampletracecatsyskerneldebugtracingtrace !〔〕(http:linuxdriver。topBlog2023202301261507400。png) TRACEEVENTCONDITION() 在某些情况下,跟踪点只有在某个条件发生时才会被调用,类似于 c if(cond) tracefoo(); TRACEEVENTCONDITION()宏就是这个作用,它和TRACEEVENT()相比只是在参数中多加了一个cond条件。TPCONDITION()会对条件做个判断。 c TRACEEVENT(name,proto,args,struct,assign,printk) TRACEEVENTCONDITION(name,proto,args,cond,struct,assign,printk) 详细使用方法可以参考traceeventssample。h。 TRACEEVENTFN() TRACEEVENTFN()是在跟踪点使能前和使能后分别打印一些信息。相比于TRACEEVENT(),TRACEEVENTFN()多了两个参数reg和unreg, c TRACEEVENT(name,proto,args,struct,assign,printk) TRACEEVENTFN(name,proto,args,struct,assign,printk,reg,unreg) reg和unreg原型为 c voidreg(void) reg函数在跟踪点使能前打印,unreg函数在跟踪点使能后打印。reg和unreg可以根据实际情况置其中一个为NULL,也可以全部置为NULL。 详细使用方法可以参考traceeventssample。h。 本文参考 samplestraceevents