SQLHIST(1) libtracefs Manual SQLHIST(1)

sqlhist - ابزاری مبتنی بر زبان SQL برای ساخت یا نمایش نحوه ساخت هیستوگرام‌های tracefs و رویدادهای ساختگی.

sqlhist [OPTIONS] [SQL-select-command]

ابزار sqlhist(1) یک عبارت مشابه SQL را دریافت کرده تا هیستوگرام‌های tracefs و رویدادهای ساختگی (synthetic events) ایجاد کند که می‌توانند اقدامات مختلفی را برای مدیریت‌های گوناگون داده‌ها به انجام برسانند.

سیستم‌فایل tracefs با زیرساخت ردگیری لینوکس (Linux tracing) که رویدادهای پویای و ایستای مختلفی در سراسر هسته دارد در تعامل است. به هر یک از این رویدادها می‌توان یک «هیستوگرام» متصل کرد، جایی که فیلدهای رویداد دسته‌ها (buckets) هیستوگرام را تعریف می‌کنند.

یک رویداد ساختگی (synthetic event) روشی برای پیوند دادن دو رویداد مجزا و استفاده از فیلدها و برچسب‌های زمانی آن رویدادها برای ساخت یک رویداد پویای جدید است. این رویداد پویای جدید، یک رویداد ساختگی نامیده می‌شود. فیلدهای هر رویداد می‌توانند محاسبات ساده‌ای روی خود داشته باشند؛ به عنوان مثال می‌توان مقدار اختلاف (delta) بین یک فیلد از یک رویداد تا فیلدی از رویداد دیگر را به دست آورد. این قابلیت برای برچسب‌های زمانی رویدادها نیز کار می‌کند، جایی که اختلاف زمانی بین دو رویداد نیز می‌تواند استخراج شده و درون رویداد ساختگی قرار گیرد.

اقدامات دیگری نیز بر اساس فیلدهای رویدادها قابل انجام است. برای نمونه، هنگامی که یک متغیر استفاده‌شده در ساخت رویداد ساختگی به یک مقدار بیشینه برسد یا صرفاً تغییر کند، می‌توان از بافر حلقوی هسته (kernel ring buffer) یک تصویر لحظه‌ای (snapshot) گرفت.

دستورات ایجاد هیستوگرام‌ها و رویدادهای ساختگی پیچیده بوده و به‌خاطرسپاری آن‌ها آسان نیست. از sqlhist برای تبدیل نحو SQL به دستورات مورد نیاز برای ساخت هیستوگرام یا رویداد ساختگی استفاده می‌شود.

عبارت SQL-select-command یک رشتهٔ SQL است که توسط tracefs_sql(3) تعریف می‌شود.

توجه داشته باشید که این دستور باید به عنوان کاربر ریشه (یا با sudo) اجرا شود، زیرا تعامل با دایرکتوری tracefs نیازمند دسترسی کاربر ریشه است، مگر اینکه گزینهٔ -t به همراه رونوشتی از دایرکتوری tracefs و رویدادهای آن داده شود.

ابزار sqlhist یک برنامه ساده است که کد آن در واقع در صفحه راهنمای tracefs_sql(3) قرار دارد.

-n name

نام رویداد ساختگی که باید ایجاد شود. سپس می‌توان از این رویداد مانند هر رویداد دیگری استفاده کرد و آن را از طریق trace-cmd(1) فعال نمود.

-t tracefs-dir

به‌منظور آزمایش این ابزار به عنوان کاربر غیر ریشه، می‌توان از یک رونوشت از دایرکتوری tracefs استفاده کرد و ارسال آن دایرکتوری با این گزینه امکان کارکرد برنامه را فراهم می‌کند. بدیهی است که گزینهٔ -e برای کاربر غیر ریشه کار نخواهد کرد، زیرا قادر به اجرا نخواهد بود.
# mkdir /tmp/tracing
# cp -r /sys/kernel/tracing/events /tmp/tracing
# exit
$ ./sqlhist -t /tmp/tracing ...

-e

نه تنها دستورات مربوط به ساخت هیستوگرام را نمایش می‌دهد، بلکه آن‌ها را اجرا نیز می‌کند. این کار نیازمند دسترسی کاربر ریشه است.

-f file

به جای خواندن دستورات SQL از خط فرمان، آن‌ها را از file می‌خواند. اگر file برابر با - باشد، دستورات از ورودی استاندارد خوانده می‌شوند.

-m var

هنگامی که متغیر var به یک مقدار بیشینهٔ جدید برسد، اقدام داده‌شده را انجام می‌دهد. این گزینه همراه با -c قابل استفاده نیست.

-c var

هنگامی که مقدار متغیر var تغییر کند، اقدام داده‌شده را انجام می‌دهد. این گزینه همراه با -m قابل استفاده نیست.

-s

به جای فراخوانی رویداد ساختگی، یک تصویر لحظه‌ای (snapshot) تهیه می‌کند.

-T

هم یک تصویر لحظه‌ای تهیه می‌کند و هم رویداد ساختگی را ردگیری می‌نماید.

-S fields[,fields]

فیلدهای داده‌شده را ذخیره می‌کند. فیلدها باید فیلدهایی از رویداد «پایانی» مشخص‌شده در SQL-select-command باشند.

-B instance

برای عبارت‌های ساده‌ای که فقط یک هیستوگرام تولید می‌کنند، نمونهٔ (instance) مشخص‌شده در اینجا مکانی خواهد بود که هیستوگرام در آن ایجاد می‌شود. این مقدار در فرآیند کامل ساخت رویداد ساختگی نادیده گرفته می‌شود، زیرا رویدادهای ساختگی تأثیری سراسری روی تمام نمونه‌های ردگیری دارند، در حالی که هیستوگرام‌ها فقط بر یک نمونه تأثیر می‌گذارند.

ایجاد فایل اجرایی sqlhist:

man tracefs_sql | sed -ne '/^EXAMPLE/,/FILES/ { /EXAMPLE/d ; /FILES/d ; p}' > sqlhist.c
gcc -o sqlhist sqlhist.c `pkg-config --cflags --libs libtracefs`

همان‌طور که در بالا توضیح داده شد، برای اهداف آزمایشی، یک رونوشت از دایرکتوری رویدادها ایجاد کنید:

$ mkdir /tmp/tracing
$ sudo cp -r /sys/kernel/tracing/events /tmp/tracing/
$ sudo chmod -R 0644 /tmp/tracing/

برای نمونه‌ای از خروجی ساده هیستوگرام با استفاده از رونوشت دایرکتوری tracefs.

$ ./sqlhist -t /tmp/tracing/ 'SELECT CAST(call_site as SYM-OFFSET), bytes_req, CAST(bytes_alloc AS _COUNTER_) FROM kmalloc'

خروجی زیر را تولید می‌کند:

echo 'hist:keys=call_site.sym-offset,bytes_req:vals=bytes_alloc' > /sys/kernel/tracing/events/kmem/kmalloc/trigger

که می‌تواند توسط ریشه (root) استفاده شود:

   # echo 'hist:keys=call_site.sym-offset,bytes_req:vals=bytes_alloc' > /sys/kernel/tracing/events/kmem/kmalloc/trigger
   # cat /sys/kernel/tracing/events/kmem/kmalloc/hist
# event histogram
#
# trigger info: hist:keys=call_site.sym-offset,bytes_req:vals=hitcount,bytes_alloc:sort=hitcount:size=2048 [active]
#
{ call_site: [ffffffff813f8d8a] load_elf_phdrs+0x4a/0xb0                               , bytes_req:        728 } hitcount:          1  bytes_alloc:       1024
{ call_site: [ffffffffc0c69e74] nf_ct_ext_add+0xd4/0x1d0 [nf_conntrack]                , bytes_req:        128 } hitcount:          1  bytes_alloc:        128
{ call_site: [ffffffff818355e6] dma_resv_get_fences+0xf6/0x440                         , bytes_req:          8 } hitcount:          1  bytes_alloc:          8
{ call_site: [ffffffffc06dc73f] intel_gt_get_buffer_pool+0x15f/0x290 [i915]            , bytes_req:        424 } hitcount:          1  bytes_alloc:        512
{ call_site: [ffffffff813f8d8a] load_elf_phdrs+0x4a/0xb0                               , bytes_req:        616 } hitcount:          1  bytes_alloc:       1024
{ call_site: [ffffffff8161a44c] __sg_alloc_table+0x11c/0x180                           , bytes_req:         32 } hitcount:          1  bytes_alloc:         32
{ call_site: [ffffffffc070749d] shmem_get_pages+0xad/0x5d0 [i915]                      , bytes_req:         16 } hitcount:          1  bytes_alloc:         16
{ call_site: [ffffffffc07507f5] intel_framebuffer_create+0x25/0x60 [i915]              , bytes_req:        408 } hitcount:          1  bytes_alloc:        512
{ call_site: [ffffffffc06fc20f] eb_parse+0x34f/0x910 [i915]                            , bytes_req:        408 } hitcount:          1  bytes_alloc:        512
{ call_site: [ffffffffc0700ebd] i915_gem_object_get_pages_internal+0x5d/0x270 [i915]   , bytes_req:         16 } hitcount:          1  bytes_alloc:         16
{ call_site: [ffffffffc0771188] intel_frontbuffer_get+0x38/0x220 [i915]                , bytes_req:        400 } hitcount:          1  bytes_alloc:        512
{ call_site: [ffffffff8161a44c] __sg_alloc_table+0x11c/0x180                           , bytes_req:        128 } hitcount:          1  bytes_alloc:        128
{ call_site: [ffffffff813f8f45] load_elf_binary+0x155/0x1680                           , bytes_req:         28 } hitcount:          1  bytes_alloc:         32
{ call_site: [ffffffffc07038c8] __assign_mmap_offset+0x208/0x3d0 [i915]                , bytes_req:        288 } hitcount:          1  bytes_alloc:        512
{ call_site: [ffffffff813737b2] alloc_bprm+0x32/0x2f0                                  , bytes_req:        416 } hitcount:          1  bytes_alloc:        512
{ call_site: [ffffffff813f9027] load_elf_binary+0x237/0x1680                           , bytes_req:         64 } hitcount:          1  bytes_alloc:         64
{ call_site: [ffffffff8161a44c] __sg_alloc_table+0x11c/0x180                           , bytes_req:         64 } hitcount:          1  bytes_alloc:         64
{ call_site: [ffffffffc040ffe7] drm_vma_node_allow+0x27/0xe0 [drm]                     , bytes_req:         40 } hitcount:          2  bytes_alloc:        128
{ call_site: [ffffffff813cda98] __do_sys_timerfd_create+0x58/0x1c0                     , bytes_req:        336 } hitcount:          2  bytes_alloc:       1024
{ call_site: [ffffffff818355e6] dma_resv_get_fences+0xf6/0x440                         , bytes_req:         40 } hitcount:          2  bytes_alloc:        128
{ call_site: [ffffffff8139b75a] single_open+0x2a/0xa0                                  , bytes_req:         32 } hitcount:          2  bytes_alloc:         64
{ call_site: [ffffffff815df715] bio_kmalloc+0x25/0x80                                  , bytes_req:        136 } hitcount:          2  bytes_alloc:        384
{ call_site: [ffffffffc071e5cd] i915_vma_work+0x1d/0x50 [i915]                         , bytes_req:        416 } hitcount:          3  bytes_alloc:       1536
{ call_site: [ffffffff81390d0d] alloc_fdtable+0x4d/0x100                               , bytes_req:         56 } hitcount:          3  bytes_alloc:        192
{ call_site: [ffffffffc06ff65f] i915_gem_do_execbuffer+0x158f/0x2440 [i915]            , bytes_req:         16 } hitcount:          4  bytes_alloc:         64
{ call_site: [ffffffff8137713c] alloc_pipe_info+0x5c/0x230                             , bytes_req:        384 } hitcount:          5  bytes_alloc:       2560
{ call_site: [ffffffff813771b4] alloc_pipe_info+0xd4/0x230                             , bytes_req:        640 } hitcount:          5  bytes_alloc:       5120
{ call_site: [ffffffff81834cdb] dma_resv_list_alloc+0x1b/0x40                          , bytes_req:         40 } hitcount:          6  bytes_alloc:        384
{ call_site: [ffffffff81834cdb] dma_resv_list_alloc+0x1b/0x40                          , bytes_req:         56 } hitcount:          9  bytes_alloc:        576
{ call_site: [ffffffff8120086e] tracing_map_sort_entries+0x9e/0x3e0                    , bytes_req:         24 } hitcount:         60  bytes_alloc:       1920
Totals:
    Hits: 122
    Entries: 30
    Dropped: 0

توجه داشته باشید، اگرچه مثال‌ها از حروف بزرگ برای کلیدواژه‌های SQL استفاده می‌کنند، اما الزامی به این کار نیست. SELECT می‌تواند select یا حتی sElEcT نیز باشد.

با استفاده از زبان کامل SQL، رویدادهای ساختگی (synthetic events) را می‌توان ایجاد و پردازش کرد. به عنوان مثال، با استفاده از sqlhist در کنار trace-cmd(1)، تاخیر بیدارباش (wake up latency) می‌تواند با ایجاد یک رویداد ساختگی از طریق پیوند دادن رویدادهای sched_waking و sched_switch ثبت شود.

 # sqlhist -n wakeup_lat -e -T -m lat 'SELECT end.next_comm AS comm, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS lat FROM ' \
   'sched_waking AS start JOIN sched_switch AS end ON start.pid = end.next_pid WHERE end.next_prio < 100 && end.next_comm == "cyclictest"'
 # trace-cmd start -e all -e wakeup_lat -R stacktrace
 # cyclictest -l 1000 -p80 -i250  -a -t -q -m -d 0 -b 1000 --tracemark
 # trace-cmd show -s | tail -30
         <idle>-0       [002] dNh4 23454.902246: sched_wakeup: comm=cyclictest pid=12272 prio=120 target_cpu=002
         <idle>-0       [005] ...1 23454.902246: cpu_idle: state=4294967295 cpu_id=5
         <idle>-0       [007] d..1 23454.902246: cpu_idle: state=0 cpu_id=7
         <idle>-0       [002] dNh1 23454.902247: hrtimer_expire_exit: hrtimer=0000000037956dc2
         <idle>-0       [005] d..1 23454.902248: cpu_idle: state=0 cpu_id=5
         <idle>-0       [002] dNh1 23454.902248: write_msr: 6e0, value 4866ce957272
         <idle>-0       [006] ...1 23454.902248: cpu_idle: state=4294967295 cpu_id=6
         <idle>-0       [002] dNh1 23454.902249: local_timer_exit: vector=236
         <idle>-0       [006] d..1 23454.902250: cpu_idle: state=0 cpu_id=6
         <idle>-0       [002] .N.1 23454.902250: cpu_idle: state=4294967295 cpu_id=2
         <idle>-0       [002] dN.1 23454.902251: rcu_utilization: Start context switch
         <idle>-0       [002] dN.1 23454.902252: rcu_utilization: End context switch
         <idle>-0       [001] ...1 23454.902252: cpu_idle: state=4294967295 cpu_id=1
         <idle>-0       [002] dN.3 23454.902253: prandom_u32: ret=3692516021
         <idle>-0       [001] d..1 23454.902254: cpu_idle: state=0 cpu_id=1
         <idle>-0       [002] d..2 23454.902254: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=cyclictest next_pid=12275 next_prio=19
         <idle>-0       [002] d..4 23454.902256: wakeup_lat: next_comm=cyclictest lat=17
         <idle>-0       [002] d..5 23454.902258: <stack trace>
=> trace_event_raw_event_synth
=> action_trace
=> event_hist_trigger
=> event_triggers_call
=> trace_event_buffer_commit
=> trace_event_raw_event_sched_switch
=> __traceiter_sched_switch
=> __schedule
=> schedule_idle
=> do_idle
=> cpu_startup_entry
=> secondary_startup_64_no_verify

در اینجا گزینه‌های sqlhist توضیح داده شده‌اند:

-n wakeup_lat

نام‌گذاری رویداد ساختگی مورد استفاده به wakeup_lat.

-e

اجرای دستوراتی که چاپ می‌شوند.

-T

انجام هم‌زمان یک عمل trace و سپس یک عمل snapshot (جابجایی بافر به بافر ایستان snapshot).

-m lat

راه‌اندازی (trigger) این اعمال هر زمان که lat به یک مقدار بیشینه جدید برسد.

اکنون بررسی بخش‌های دستور SQL:

'SELECT end.next_comm AS comm, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS lat FROM ' \
   'sched_waking AS start JOIN sched_switch AS end ON start.pid = end.next_pid WHERE end.next_prio < 100 && end.next_comm == "cyclictest"'

end.next_comm AS comm

ذخیره فیلد next_comm از sched_switch و قرار دادن آن در فیلد comm از رویداد ساختگی wakeup_lat. (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) AS lat
اختلاف برچسب‌های زمانی را از رویداد sched_switch و رویداد sched_waking محاسبه می‌کند. از آن‌جا که برچسب‌های زمانی معمولاً بر حسب نانوثانیه ثبت می‌شوند، TIMESTAMP برچسب زمانی کامل به نانوثانیه را ارائه می‌دهد، اما در اینجا، TIMESTAMP_USECS آن را به میکروثانیه کوتاه (truncate) می‌کند. این مقدار در متغیر lat ذخیره می‌شود، که در رویداد ترکیبی (synthetic event) نیز ثبت خواهد شد.

FROM sched_waking AS start JOIN sched_switch AS end ON start.pid = end.next_pid

با پیوند دادن (JOIN) sched_waking به sched_switch، رویداد ترکیبی را ایجاد می‌کند و فیلد pid از sched_waking را با فیلد next_pid از sched_switch تطبیق می‌دهد. همچنین start را نام مستعاری برای sched_waking و end را نام مستعاری برای sched_switch قرار می‌دهد، تا بتوان در ادامهٔ عبارت SQL از start و end به ترتیب به عنوان جایگزینی برای sched_waking و sched_switch استفاده کرد.

WHERE end.next_prio < 100 && end.next_comm == "cyclictest"

منطق را طوری فیلتر می‌کند که فقط در صورتی اجرا شود که فیلد next_prio از sched_waking کمتر از ۱۰۰ باشد. (توجه داشته باشید که در هسته، اولویت‌ها معکوس هستند و اولویت‌های بی‌درنگ از ۰ تا ۱۰۰ نشان داده می‌شوند که در آن ۰ بالاترین اولویت است). همچنین تنها زمانی ردگیری انجام می‌شود که next_comm (وظیفه‌ای که زمان‌بندی شده و به اجرا درمی‌آید) از رویداد sched_switch نام "cyclictest" را داشته باشد.

برای دستور trace-cmd(3):

trace-cmd start -e all -e wakeup_lat -R stacktrace

trace-cmd start

ردگیری را فعال می‌کند (در فایل ذخیره نمی‌کند).

-e all

تمام رویدادها را فعال می‌کند.

-e wakeup_lat -R stacktrace

باعث می‌شود رویداد "wakeup_lat" (رویداد ترکیبی ما) تریگر (trigger) stacktrace را فعال کند، به طوری که به ازای هر نمونه از رویداد "wakeup_lat"، یک رد پشتهٔ هسته (kernel stack trace) در بافر حلقه‌ای (ring buffer) ثبت خواهد شد.

پس از فراخوانی cyclictest (یک ابزار بی‌درنگ برای اندازه‌گیری تأخیر بیدارباش)، بافر تصویر لحظه‌ای (snapshot) را بخوانید.

trace-cmd show -s

دستور trace-cmd show بافر حلقه‌ای هسته را می‌خواند، و گزینهٔ -s به جای بافر معمولی، بافر snapshot را می‌خواند.
<idle>-0       [002] d..4 23454.902256: wakeup_lat: next_comm=cyclictest lat=17
می‌بینیم که رویداد "wakeup_lat" روی CPU 2 با تأخیر بیدارباش ۱۷ میکروثانیه رخ داده است.

این را می‌توان در یک فایل trace.dat استخراج کرد که trace-cmd(3) و همچنین kernelshark بتوانند آن را بخوانند و تحلیل‌های بیشتری انجام دهند.

    # trace-cmd extract -s
    # trace-cmd report --cpu 2 | tail -30
          <idle>-0     [002] 23454.902238: prandom_u32:          ret=1633425088
          <idle>-0     [002] 23454.902239: sched_wakeup:         cyclictest:12275 [19] CPU:002
          <idle>-0     [002] 23454.902241: hrtimer_expire_exit:  hrtimer=0xffffbbd68286fe60
          <idle>-0     [002] 23454.902241: hrtimer_cancel:       hrtimer=0xffffbbd6826efe70
          <idle>-0     [002] 23454.902242: hrtimer_expire_entry: hrtimer=0xffffbbd6826efe70 now=23455294430750 function=hrtimer_wakeup/0x0
          <idle>-0     [002] 23454.902243: sched_waking:         comm=cyclictest pid=12272 prio=120 target_cpu=002
          <idle>-0     [002] 23454.902244: prandom_u32:          ret=1102749734
          <idle>-0     [002] 23454.902246: sched_wakeup:         cyclictest:12272 [120] CPU:002
          <idle>-0     [002] 23454.902247: hrtimer_expire_exit:  hrtimer=0xffffbbd6826efe70
          <idle>-0     [002] 23454.902248: write_msr:            6e0, value 4866ce957272
          <idle>-0     [002] 23454.902249: local_timer_exit:     vector=236
          <idle>-0     [002] 23454.902250: cpu_idle:             state=4294967295 cpu_id=2
          <idle>-0     [002] 23454.902251: rcu_utilization:      Start context switch
          <idle>-0     [002] 23454.902252: rcu_utilization:      End context switch
          <idle>-0     [002] 23454.902253: prandom_u32:          ret=3692516021
          <idle>-0     [002] 23454.902254: sched_switch:         swapper/2:0 [120] R ==> cyclictest:12275 [19]
          <idle>-0     [002] 23454.902256: wakeup_lat:           next_comm=cyclictest lat=17
          <idle>-0     [002] 23454.902258: kernel_stack:         <stack trace >
=> trace_event_raw_event_synth (ffffffff8121a0db)
=> action_trace (ffffffff8121e9fb)
=> event_hist_trigger (ffffffff8121ca8d)
=> event_triggers_call (ffffffff81216c72)
=> trace_event_buffer_commit (ffffffff811f7618)
=> trace_event_raw_event_sched_switch (ffffffff8110fda4)
=> __traceiter_sched_switch (ffffffff8110d449)
=> __schedule (ffffffff81c02002)
=> schedule_idle (ffffffff81c02c86)
=> do_idle (ffffffff8111e898)
=> cpu_startup_entry (ffffffff8111eba9)
=> secondary_startup_64_no_verify (ffffffff81000107)

از آنجا که sqlhist صرفاً یک کد نمونه از یک صفحه man است، قطعاً حاوی باگ‌های زیادی خواهد بود. از جمله این‌که تمامی مسیرهای خطا به‌درستی پوشش داده نشده‌اند.

trace-cmd(1), tracefs_sql(3)

نوشته‌شده توسط Steven Rostedt، <rostedt@goodmis.org[1]>

https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git

کپی‌رایت (C) 2021 , Inc. استفاده آزاد از این نرم‌افزار تحت شرایط مجوز عمومی گنو (GPL) مجاز است.

1.
rostedt@goodmis.org
mailto:rostedt@goodmis.org
01/05/2026 libtracefs 1.8.3