forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ftrace.txt
1841 lines (1476 loc) · 65.1 KB
/
ftrace.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
ftrace - Function Tracer
========================
Copyright 2008 Red Hat Inc.
Author: Steven Rostedt <[email protected]>
License: The GNU Free Documentation License, Version 1.2
(dual licensed under the GPL v2)
Reviewers: Elias Oltmanns, Randy Dunlap, Andrew Morton,
John Kacur, and David Teigland.
Written for: 2.6.28-rc2
Introduction
------------
Ftrace is an internal tracer designed to help out developers and
designers of systems to find what is going on inside the kernel.
It can be used for debugging or analyzing latencies and
performance issues that take place outside of user-space.
Although ftrace is the function tracer, it also includes an
infrastructure that allows for other types of tracing. Some of
the tracers that are currently in ftrace include a tracer to
trace context switches, the time it takes for a high priority
task to run after it was woken up, the time interrupts are
disabled, and more (ftrace allows for tracer plugins, which
means that the list of tracers can always grow).
Implementation Details
----------------------
See ftrace-design.txt for details for arch porters and such.
The File System
---------------
Ftrace uses the debugfs file system to hold the control files as
well as the files to display output.
When debugfs is configured into the kernel (which selecting any ftrace
option will do) the directory /sys/kernel/debug will be created. To mount
this directory, you can add to your /etc/fstab file:
debugfs /sys/kernel/debug debugfs defaults 0 0
Or you can mount it at run time with:
mount -t debugfs nodev /sys/kernel/debug
For quicker access to that directory you may want to make a soft link to
it:
ln -s /sys/kernel/debug /debug
Any selected ftrace option will also create a directory called tracing
within the debugfs. The rest of the document will assume that you are in
the ftrace directory (cd /sys/kernel/debug/tracing) and will only concentrate
on the files within that directory and not distract from the content with
the extended "/sys/kernel/debug/tracing" path name.
That's it! (assuming that you have ftrace configured into your kernel)
After mounting the debugfs, you can see a directory called
"tracing". This directory contains the control and output files
of ftrace. Here is a list of some of the key files:
Note: all time values are in microseconds.
current_tracer:
This is used to set or display the current tracer
that is configured.
available_tracers:
This holds the different types of tracers that
have been compiled into the kernel. The
tracers listed here can be configured by
echoing their name into current_tracer.
tracing_on:
This sets or displays whether writing to the trace
ring buffer is enabled. Echo 0 into this file to disable
the tracer or 1 to enable it.
trace:
This file holds the output of the trace in a human
readable format (described below).
trace_pipe:
The output is the same as the "trace" file but this
file is meant to be streamed with live tracing.
Reads from this file will block until new data is
retrieved. Unlike the "trace" file, this file is a
consumer. This means reading from this file causes
sequential reads to display more current data. Once
data is read from this file, it is consumed, and
will not be read again with a sequential read. The
"trace" file is static, and if the tracer is not
adding more data,they will display the same
information every time they are read.
trace_options:
This file lets the user control the amount of data
that is displayed in one of the above output
files.
tracing_max_latency:
Some of the tracers record the max latency.
For example, the time interrupts are disabled.
This time is saved in this file. The max trace
will also be stored, and displayed by "trace".
A new max trace will only be recorded if the
latency is greater than the value in this
file. (in microseconds)
buffer_size_kb:
This sets or displays the number of kilobytes each CPU
buffer can hold. The tracer buffers are the same size
for each CPU. The displayed number is the size of the
CPU buffer and not total size of all buffers. The
trace buffers are allocated in pages (blocks of memory
that the kernel uses for allocation, usually 4 KB in size).
If the last page allocated has room for more bytes
than requested, the rest of the page will be used,
making the actual allocation bigger than requested.
( Note, the size may not be a multiple of the page size
due to buffer management overhead. )
This can only be updated when the current_tracer
is set to "nop".
tracing_cpumask:
This is a mask that lets the user only trace
on specified CPUS. The format is a hex string
representing the CPUS.
set_ftrace_filter:
When dynamic ftrace is configured in (see the
section below "dynamic ftrace"), the code is dynamically
modified (code text rewrite) to disable calling of the
function profiler (mcount). This lets tracing be configured
in with practically no overhead in performance. This also
has a side effect of enabling or disabling specific functions
to be traced. Echoing names of functions into this file
will limit the trace to only those functions.
This interface also allows for commands to be used. See the
"Filter commands" section for more details.
set_ftrace_notrace:
This has an effect opposite to that of
set_ftrace_filter. Any function that is added here will not
be traced. If a function exists in both set_ftrace_filter
and set_ftrace_notrace, the function will _not_ be traced.
set_ftrace_pid:
Have the function tracer only trace a single thread.
set_graph_function:
Set a "trigger" function where tracing should start
with the function graph tracer (See the section
"dynamic ftrace" for more details).
available_filter_functions:
This lists the functions that ftrace
has processed and can trace. These are the function
names that you can pass to "set_ftrace_filter" or
"set_ftrace_notrace". (See the section "dynamic ftrace"
below for more details.)
The Tracers
-----------
Here is the list of current tracers that may be configured.
"function"
Function call tracer to trace all kernel functions.
"function_graph"
Similar to the function tracer except that the
function tracer probes the functions on their entry
whereas the function graph tracer traces on both entry
and exit of the functions. It then provides the ability
to draw a graph of function calls similar to C code
source.
"irqsoff"
Traces the areas that disable interrupts and saves
the trace with the longest max latency.
See tracing_max_latency. When a new max is recorded,
it replaces the old trace. It is best to view this
trace with the latency-format option enabled.
"preemptoff"
Similar to irqsoff but traces and records the amount of
time for which preemption is disabled.
"preemptirqsoff"
Similar to irqsoff and preemptoff, but traces and
records the largest time for which irqs and/or preemption
is disabled.
"wakeup"
Traces and records the max latency that it takes for
the highest priority task to get scheduled after
it has been woken up.
"hw-branch-tracer"
Uses the BTS CPU feature on x86 CPUs to traces all
branches executed.
"nop"
This is the "trace nothing" tracer. To remove all
tracers from tracing simply echo "nop" into
current_tracer.
Examples of using the tracer
----------------------------
Here are typical examples of using the tracers when controlling
them only with the debugfs interface (without using any
user-land utilities).
Output format:
--------------
Here is an example of the output format of the file "trace"
--------
# tracer: function
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
bash-4251 [01] 10152.583854: path_put <-path_walk
bash-4251 [01] 10152.583855: dput <-path_put
bash-4251 [01] 10152.583855: _atomic_dec_and_lock <-dput
--------
A header is printed with the tracer name that is represented by
the trace. In this case the tracer is "function". Then a header
showing the format. Task name "bash", the task PID "4251", the
CPU that it was running on "01", the timestamp in <secs>.<usecs>
format, the function name that was traced "path_put" and the
parent function that called this function "path_walk". The
timestamp is the time at which the function was entered.
Latency trace format
--------------------
When the latency-format option is enabled, the trace file gives
somewhat more information to see why a latency happened.
Here is a typical trace.
# tracer: irqsoff
#
irqsoff latency trace v1.1.5 on 2.6.26-rc8
--------------------------------------------------------------------
latency: 97 us, #3/3, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2)
-----------------
| task: swapper-0 (uid:0 nice:0 policy:0 rt_prio:0)
-----------------
=> started at: apic_timer_interrupt
=> ended at: do_softirq
# _------=> CPU#
# / _-----=> irqs-off
# | / _----=> need-resched
# || / _---=> hardirq/softirq
# ||| / _--=> preempt-depth
# |||| /
# ||||| delay
# cmd pid ||||| time | caller
# \ / ||||| \ | /
<idle>-0 0d..1 0us+: trace_hardirqs_off_thunk (apic_timer_interrupt)
<idle>-0 0d.s. 97us : __do_softirq (do_softirq)
<idle>-0 0d.s1 98us : trace_hardirqs_on (do_softirq)
This shows that the current tracer is "irqsoff" tracing the time
for which interrupts were disabled. It gives the trace version
and the version of the kernel upon which this was executed on
(2.6.26-rc8). Then it displays the max latency in microsecs (97
us). The number of trace entries displayed and the total number
recorded (both are three: #3/3). The type of preemption that was
used (PREEMPT). VP, KP, SP, and HP are always zero and are
reserved for later use. #P is the number of online CPUS (#P:2).
The task is the process that was running when the latency
occurred. (swapper pid: 0).
The start and stop (the functions in which the interrupts were
disabled and enabled respectively) that caused the latencies:
apic_timer_interrupt is where the interrupts were disabled.
do_softirq is where they were enabled again.
The next lines after the header are the trace itself. The header
explains which is which.
cmd: The name of the process in the trace.
pid: The PID of that process.
CPU#: The CPU which the process was running on.
irqs-off: 'd' interrupts are disabled. '.' otherwise.
Note: If the architecture does not support a way to
read the irq flags variable, an 'X' will always
be printed here.
need-resched: 'N' task need_resched is set, '.' otherwise.
hardirq/softirq:
'H' - hard irq occurred inside a softirq.
'h' - hard irq is running
's' - soft irq is running
'.' - normal context.
preempt-depth: The level of preempt_disabled
The above is mostly meaningful for kernel developers.
time: When the latency-format option is enabled, the trace file
output includes a timestamp relative to the start of the
trace. This differs from the output when latency-format
is disabled, which includes an absolute timestamp.
delay: This is just to help catch your eye a bit better. And
needs to be fixed to be only relative to the same CPU.
The marks are determined by the difference between this
current trace and the next trace.
'!' - greater than preempt_mark_thresh (default 100)
'+' - greater than 1 microsecond
' ' - less than or equal to 1 microsecond.
The rest is the same as the 'trace' file.
trace_options
-------------
The trace_options file is used to control what gets printed in
the trace output. To see what is available, simply cat the file:
cat trace_options
print-parent nosym-offset nosym-addr noverbose noraw nohex nobin \
noblock nostacktrace nosched-tree nouserstacktrace nosym-userobj
To disable one of the options, echo in the option prepended with
"no".
echo noprint-parent > trace_options
To enable an option, leave off the "no".
echo sym-offset > trace_options
Here are the available options:
print-parent - On function traces, display the calling (parent)
function as well as the function being traced.
print-parent:
bash-4000 [01] 1477.606694: simple_strtoul <-strict_strtoul
noprint-parent:
bash-4000 [01] 1477.606694: simple_strtoul
sym-offset - Display not only the function name, but also the
offset in the function. For example, instead of
seeing just "ktime_get", you will see
"ktime_get+0xb/0x20".
sym-offset:
bash-4000 [01] 1477.606694: simple_strtoul+0x6/0xa0
sym-addr - this will also display the function address as well
as the function name.
sym-addr:
bash-4000 [01] 1477.606694: simple_strtoul <c0339346>
verbose - This deals with the trace file when the
latency-format option is enabled.
bash 4000 1 0 00000000 00010a95 [58127d26] 1720.415ms \
(+0.000ms): simple_strtoul (strict_strtoul)
raw - This will display raw numbers. This option is best for
use with user applications that can translate the raw
numbers better than having it done in the kernel.
hex - Similar to raw, but the numbers will be in a hexadecimal
format.
bin - This will print out the formats in raw binary.
block - TBD (needs update)
stacktrace - This is one of the options that changes the trace
itself. When a trace is recorded, so is the stack
of functions. This allows for back traces of
trace sites.
userstacktrace - This option changes the trace. It records a
stacktrace of the current userspace thread.
sym-userobj - when user stacktrace are enabled, look up which
object the address belongs to, and print a
relative address. This is especially useful when
ASLR is on, otherwise you don't get a chance to
resolve the address to object/file/line after
the app is no longer running
The lookup is performed when you read
trace,trace_pipe. Example:
a.out-1623 [000] 40874.465068: /root/a.out[+0x480] <-/root/a.out[+0
x494] <- /root/a.out[+0x4a8] <- /lib/libc-2.7.so[+0x1e1a6]
sched-tree - trace all tasks that are on the runqueue, at
every scheduling event. Will add overhead if
there's a lot of tasks running at once.
latency-format - This option changes the trace. When
it is enabled, the trace displays
additional information about the
latencies, as described in "Latency
trace format".
overwrite - This controls what happens when the trace buffer is
full. If "1" (default), the oldest events are
discarded and overwritten. If "0", then the newest
events are discarded.
ftrace_enabled
--------------
The following tracers (listed below) give different output
depending on whether or not the sysctl ftrace_enabled is set. To
set ftrace_enabled, one can either use the sysctl function or
set it via the proc file system interface.
sysctl kernel.ftrace_enabled=1
or
echo 1 > /proc/sys/kernel/ftrace_enabled
To disable ftrace_enabled simply replace the '1' with '0' in the
above commands.
When ftrace_enabled is set the tracers will also record the
functions that are within the trace. The descriptions of the
tracers will also show an example with ftrace enabled.
irqsoff
-------
When interrupts are disabled, the CPU can not react to any other
external event (besides NMIs and SMIs). This prevents the timer
interrupt from triggering or the mouse interrupt from letting
the kernel know of a new mouse event. The result is a latency
with the reaction time.
The irqsoff tracer tracks the time for which interrupts are
disabled. When a new maximum latency is hit, the tracer saves
the trace leading up to that latency point so that every time a
new maximum is reached, the old saved trace is discarded and the
new trace is saved.
To reset the maximum, echo 0 into tracing_max_latency. Here is
an example:
# echo irqsoff > current_tracer
# echo latency-format > trace_options
# echo 0 > tracing_max_latency
# echo 1 > tracing_on
# ls -ltr
[...]
# echo 0 > tracing_on
# cat trace
# tracer: irqsoff
#
irqsoff latency trace v1.1.5 on 2.6.26
--------------------------------------------------------------------
latency: 12 us, #3/3, CPU#1 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2)
-----------------
| task: bash-3730 (uid:0 nice:0 policy:0 rt_prio:0)
-----------------
=> started at: sys_setpgid
=> ended at: sys_setpgid
# _------=> CPU#
# / _-----=> irqs-off
# | / _----=> need-resched
# || / _---=> hardirq/softirq
# ||| / _--=> preempt-depth
# |||| /
# ||||| delay
# cmd pid ||||| time | caller
# \ / ||||| \ | /
bash-3730 1d... 0us : _write_lock_irq (sys_setpgid)
bash-3730 1d..1 1us+: _write_unlock_irq (sys_setpgid)
bash-3730 1d..2 14us : trace_hardirqs_on (sys_setpgid)
Here we see that that we had a latency of 12 microsecs (which is
very good). The _write_lock_irq in sys_setpgid disabled
interrupts. The difference between the 12 and the displayed
timestamp 14us occurred because the clock was incremented
between the time of recording the max latency and the time of
recording the function that had that latency.
Note the above example had ftrace_enabled not set. If we set the
ftrace_enabled, we get a much larger output:
# tracer: irqsoff
#
irqsoff latency trace v1.1.5 on 2.6.26-rc8
--------------------------------------------------------------------
latency: 50 us, #101/101, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2)
-----------------
| task: ls-4339 (uid:0 nice:0 policy:0 rt_prio:0)
-----------------
=> started at: __alloc_pages_internal
=> ended at: __alloc_pages_internal
# _------=> CPU#
# / _-----=> irqs-off
# | / _----=> need-resched
# || / _---=> hardirq/softirq
# ||| / _--=> preempt-depth
# |||| /
# ||||| delay
# cmd pid ||||| time | caller
# \ / ||||| \ | /
ls-4339 0...1 0us+: get_page_from_freelist (__alloc_pages_internal)
ls-4339 0d..1 3us : rmqueue_bulk (get_page_from_freelist)
ls-4339 0d..1 3us : _spin_lock (rmqueue_bulk)
ls-4339 0d..1 4us : add_preempt_count (_spin_lock)
ls-4339 0d..2 4us : __rmqueue (rmqueue_bulk)
ls-4339 0d..2 5us : __rmqueue_smallest (__rmqueue)
ls-4339 0d..2 5us : __mod_zone_page_state (__rmqueue_smallest)
ls-4339 0d..2 6us : __rmqueue (rmqueue_bulk)
ls-4339 0d..2 6us : __rmqueue_smallest (__rmqueue)
ls-4339 0d..2 7us : __mod_zone_page_state (__rmqueue_smallest)
ls-4339 0d..2 7us : __rmqueue (rmqueue_bulk)
ls-4339 0d..2 8us : __rmqueue_smallest (__rmqueue)
[...]
ls-4339 0d..2 46us : __rmqueue_smallest (__rmqueue)
ls-4339 0d..2 47us : __mod_zone_page_state (__rmqueue_smallest)
ls-4339 0d..2 47us : __rmqueue (rmqueue_bulk)
ls-4339 0d..2 48us : __rmqueue_smallest (__rmqueue)
ls-4339 0d..2 48us : __mod_zone_page_state (__rmqueue_smallest)
ls-4339 0d..2 49us : _spin_unlock (rmqueue_bulk)
ls-4339 0d..2 49us : sub_preempt_count (_spin_unlock)
ls-4339 0d..1 50us : get_page_from_freelist (__alloc_pages_internal)
ls-4339 0d..2 51us : trace_hardirqs_on (__alloc_pages_internal)
Here we traced a 50 microsecond latency. But we also see all the
functions that were called during that time. Note that by
enabling function tracing, we incur an added overhead. This
overhead may extend the latency times. But nevertheless, this
trace has provided some very helpful debugging information.
preemptoff
----------
When preemption is disabled, we may be able to receive
interrupts but the task cannot be preempted and a higher
priority task must wait for preemption to be enabled again
before it can preempt a lower priority task.
The preemptoff tracer traces the places that disable preemption.
Like the irqsoff tracer, it records the maximum latency for
which preemption was disabled. The control of preemptoff tracer
is much like the irqsoff tracer.
# echo preemptoff > current_tracer
# echo latency-format > trace_options
# echo 0 > tracing_max_latency
# echo 1 > tracing_on
# ls -ltr
[...]
# echo 0 > tracing_on
# cat trace
# tracer: preemptoff
#
preemptoff latency trace v1.1.5 on 2.6.26-rc8
--------------------------------------------------------------------
latency: 29 us, #3/3, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2)
-----------------
| task: sshd-4261 (uid:0 nice:0 policy:0 rt_prio:0)
-----------------
=> started at: do_IRQ
=> ended at: __do_softirq
# _------=> CPU#
# / _-----=> irqs-off
# | / _----=> need-resched
# || / _---=> hardirq/softirq
# ||| / _--=> preempt-depth
# |||| /
# ||||| delay
# cmd pid ||||| time | caller
# \ / ||||| \ | /
sshd-4261 0d.h. 0us+: irq_enter (do_IRQ)
sshd-4261 0d.s. 29us : _local_bh_enable (__do_softirq)
sshd-4261 0d.s1 30us : trace_preempt_on (__do_softirq)
This has some more changes. Preemption was disabled when an
interrupt came in (notice the 'h'), and was enabled while doing
a softirq. (notice the 's'). But we also see that interrupts
have been disabled when entering the preempt off section and
leaving it (the 'd'). We do not know if interrupts were enabled
in the mean time.
# tracer: preemptoff
#
preemptoff latency trace v1.1.5 on 2.6.26-rc8
--------------------------------------------------------------------
latency: 63 us, #87/87, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2)
-----------------
| task: sshd-4261 (uid:0 nice:0 policy:0 rt_prio:0)
-----------------
=> started at: remove_wait_queue
=> ended at: __do_softirq
# _------=> CPU#
# / _-----=> irqs-off
# | / _----=> need-resched
# || / _---=> hardirq/softirq
# ||| / _--=> preempt-depth
# |||| /
# ||||| delay
# cmd pid ||||| time | caller
# \ / ||||| \ | /
sshd-4261 0d..1 0us : _spin_lock_irqsave (remove_wait_queue)
sshd-4261 0d..1 1us : _spin_unlock_irqrestore (remove_wait_queue)
sshd-4261 0d..1 2us : do_IRQ (common_interrupt)
sshd-4261 0d..1 2us : irq_enter (do_IRQ)
sshd-4261 0d..1 2us : idle_cpu (irq_enter)
sshd-4261 0d..1 3us : add_preempt_count (irq_enter)
sshd-4261 0d.h1 3us : idle_cpu (irq_enter)
sshd-4261 0d.h. 4us : handle_fasteoi_irq (do_IRQ)
[...]
sshd-4261 0d.h. 12us : add_preempt_count (_spin_lock)
sshd-4261 0d.h1 12us : ack_ioapic_quirk_irq (handle_fasteoi_irq)
sshd-4261 0d.h1 13us : move_native_irq (ack_ioapic_quirk_irq)
sshd-4261 0d.h1 13us : _spin_unlock (handle_fasteoi_irq)
sshd-4261 0d.h1 14us : sub_preempt_count (_spin_unlock)
sshd-4261 0d.h1 14us : irq_exit (do_IRQ)
sshd-4261 0d.h1 15us : sub_preempt_count (irq_exit)
sshd-4261 0d..2 15us : do_softirq (irq_exit)
sshd-4261 0d... 15us : __do_softirq (do_softirq)
sshd-4261 0d... 16us : __local_bh_disable (__do_softirq)
sshd-4261 0d... 16us+: add_preempt_count (__local_bh_disable)
sshd-4261 0d.s4 20us : add_preempt_count (__local_bh_disable)
sshd-4261 0d.s4 21us : sub_preempt_count (local_bh_enable)
sshd-4261 0d.s5 21us : sub_preempt_count (local_bh_enable)
[...]
sshd-4261 0d.s6 41us : add_preempt_count (__local_bh_disable)
sshd-4261 0d.s6 42us : sub_preempt_count (local_bh_enable)
sshd-4261 0d.s7 42us : sub_preempt_count (local_bh_enable)
sshd-4261 0d.s5 43us : add_preempt_count (__local_bh_disable)
sshd-4261 0d.s5 43us : sub_preempt_count (local_bh_enable_ip)
sshd-4261 0d.s6 44us : sub_preempt_count (local_bh_enable_ip)
sshd-4261 0d.s5 44us : add_preempt_count (__local_bh_disable)
sshd-4261 0d.s5 45us : sub_preempt_count (local_bh_enable)
[...]
sshd-4261 0d.s. 63us : _local_bh_enable (__do_softirq)
sshd-4261 0d.s1 64us : trace_preempt_on (__do_softirq)
The above is an example of the preemptoff trace with
ftrace_enabled set. Here we see that interrupts were disabled
the entire time. The irq_enter code lets us know that we entered
an interrupt 'h'. Before that, the functions being traced still
show that it is not in an interrupt, but we can see from the
functions themselves that this is not the case.
Notice that __do_softirq when called does not have a
preempt_count. It may seem that we missed a preempt enabling.
What really happened is that the preempt count is held on the
thread's stack and we switched to the softirq stack (4K stacks
in effect). The code does not copy the preempt count, but
because interrupts are disabled, we do not need to worry about
it. Having a tracer like this is good for letting people know
what really happens inside the kernel.
preemptirqsoff
--------------
Knowing the locations that have interrupts disabled or
preemption disabled for the longest times is helpful. But
sometimes we would like to know when either preemption and/or
interrupts are disabled.
Consider the following code:
local_irq_disable();
call_function_with_irqs_off();
preempt_disable();
call_function_with_irqs_and_preemption_off();
local_irq_enable();
call_function_with_preemption_off();
preempt_enable();
The irqsoff tracer will record the total length of
call_function_with_irqs_off() and
call_function_with_irqs_and_preemption_off().
The preemptoff tracer will record the total length of
call_function_with_irqs_and_preemption_off() and
call_function_with_preemption_off().
But neither will trace the time that interrupts and/or
preemption is disabled. This total time is the time that we can
not schedule. To record this time, use the preemptirqsoff
tracer.
Again, using this trace is much like the irqsoff and preemptoff
tracers.
# echo preemptirqsoff > current_tracer
# echo latency-format > trace_options
# echo 0 > tracing_max_latency
# echo 1 > tracing_on
# ls -ltr
[...]
# echo 0 > tracing_on
# cat trace
# tracer: preemptirqsoff
#
preemptirqsoff latency trace v1.1.5 on 2.6.26-rc8
--------------------------------------------------------------------
latency: 293 us, #3/3, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2)
-----------------
| task: ls-4860 (uid:0 nice:0 policy:0 rt_prio:0)
-----------------
=> started at: apic_timer_interrupt
=> ended at: __do_softirq
# _------=> CPU#
# / _-----=> irqs-off
# | / _----=> need-resched
# || / _---=> hardirq/softirq
# ||| / _--=> preempt-depth
# |||| /
# ||||| delay
# cmd pid ||||| time | caller
# \ / ||||| \ | /
ls-4860 0d... 0us!: trace_hardirqs_off_thunk (apic_timer_interrupt)
ls-4860 0d.s. 294us : _local_bh_enable (__do_softirq)
ls-4860 0d.s1 294us : trace_preempt_on (__do_softirq)
The trace_hardirqs_off_thunk is called from assembly on x86 when
interrupts are disabled in the assembly code. Without the
function tracing, we do not know if interrupts were enabled
within the preemption points. We do see that it started with
preemption enabled.
Here is a trace with ftrace_enabled set:
# tracer: preemptirqsoff
#
preemptirqsoff latency trace v1.1.5 on 2.6.26-rc8
--------------------------------------------------------------------
latency: 105 us, #183/183, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2)
-----------------
| task: sshd-4261 (uid:0 nice:0 policy:0 rt_prio:0)
-----------------
=> started at: write_chan
=> ended at: __do_softirq
# _------=> CPU#
# / _-----=> irqs-off
# | / _----=> need-resched
# || / _---=> hardirq/softirq
# ||| / _--=> preempt-depth
# |||| /
# ||||| delay
# cmd pid ||||| time | caller
# \ / ||||| \ | /
ls-4473 0.N.. 0us : preempt_schedule (write_chan)
ls-4473 0dN.1 1us : _spin_lock (schedule)
ls-4473 0dN.1 2us : add_preempt_count (_spin_lock)
ls-4473 0d..2 2us : put_prev_task_fair (schedule)
[...]
ls-4473 0d..2 13us : set_normalized_timespec (ktime_get_ts)
ls-4473 0d..2 13us : __switch_to (schedule)
sshd-4261 0d..2 14us : finish_task_switch (schedule)
sshd-4261 0d..2 14us : _spin_unlock_irq (finish_task_switch)
sshd-4261 0d..1 15us : add_preempt_count (_spin_lock_irqsave)
sshd-4261 0d..2 16us : _spin_unlock_irqrestore (hrtick_set)
sshd-4261 0d..2 16us : do_IRQ (common_interrupt)
sshd-4261 0d..2 17us : irq_enter (do_IRQ)
sshd-4261 0d..2 17us : idle_cpu (irq_enter)
sshd-4261 0d..2 18us : add_preempt_count (irq_enter)
sshd-4261 0d.h2 18us : idle_cpu (irq_enter)
sshd-4261 0d.h. 18us : handle_fasteoi_irq (do_IRQ)
sshd-4261 0d.h. 19us : _spin_lock (handle_fasteoi_irq)
sshd-4261 0d.h. 19us : add_preempt_count (_spin_lock)
sshd-4261 0d.h1 20us : _spin_unlock (handle_fasteoi_irq)
sshd-4261 0d.h1 20us : sub_preempt_count (_spin_unlock)
[...]
sshd-4261 0d.h1 28us : _spin_unlock (handle_fasteoi_irq)
sshd-4261 0d.h1 29us : sub_preempt_count (_spin_unlock)
sshd-4261 0d.h2 29us : irq_exit (do_IRQ)
sshd-4261 0d.h2 29us : sub_preempt_count (irq_exit)
sshd-4261 0d..3 30us : do_softirq (irq_exit)
sshd-4261 0d... 30us : __do_softirq (do_softirq)
sshd-4261 0d... 31us : __local_bh_disable (__do_softirq)
sshd-4261 0d... 31us+: add_preempt_count (__local_bh_disable)
sshd-4261 0d.s4 34us : add_preempt_count (__local_bh_disable)
[...]
sshd-4261 0d.s3 43us : sub_preempt_count (local_bh_enable_ip)
sshd-4261 0d.s4 44us : sub_preempt_count (local_bh_enable_ip)
sshd-4261 0d.s3 44us : smp_apic_timer_interrupt (apic_timer_interrupt)
sshd-4261 0d.s3 45us : irq_enter (smp_apic_timer_interrupt)
sshd-4261 0d.s3 45us : idle_cpu (irq_enter)
sshd-4261 0d.s3 46us : add_preempt_count (irq_enter)
sshd-4261 0d.H3 46us : idle_cpu (irq_enter)
sshd-4261 0d.H3 47us : hrtimer_interrupt (smp_apic_timer_interrupt)
sshd-4261 0d.H3 47us : ktime_get (hrtimer_interrupt)
[...]
sshd-4261 0d.H3 81us : tick_program_event (hrtimer_interrupt)
sshd-4261 0d.H3 82us : ktime_get (tick_program_event)
sshd-4261 0d.H3 82us : ktime_get_ts (ktime_get)
sshd-4261 0d.H3 83us : getnstimeofday (ktime_get_ts)
sshd-4261 0d.H3 83us : set_normalized_timespec (ktime_get_ts)
sshd-4261 0d.H3 84us : clockevents_program_event (tick_program_event)
sshd-4261 0d.H3 84us : lapic_next_event (clockevents_program_event)
sshd-4261 0d.H3 85us : irq_exit (smp_apic_timer_interrupt)
sshd-4261 0d.H3 85us : sub_preempt_count (irq_exit)
sshd-4261 0d.s4 86us : sub_preempt_count (irq_exit)
sshd-4261 0d.s3 86us : add_preempt_count (__local_bh_disable)
[...]
sshd-4261 0d.s1 98us : sub_preempt_count (net_rx_action)
sshd-4261 0d.s. 99us : add_preempt_count (_spin_lock_irq)
sshd-4261 0d.s1 99us+: _spin_unlock_irq (run_timer_softirq)
sshd-4261 0d.s. 104us : _local_bh_enable (__do_softirq)
sshd-4261 0d.s. 104us : sub_preempt_count (_local_bh_enable)
sshd-4261 0d.s. 105us : _local_bh_enable (__do_softirq)
sshd-4261 0d.s1 105us : trace_preempt_on (__do_softirq)
This is a very interesting trace. It started with the preemption
of the ls task. We see that the task had the "need_resched" bit
set via the 'N' in the trace. Interrupts were disabled before
the spin_lock at the beginning of the trace. We see that a
schedule took place to run sshd. When the interrupts were
enabled, we took an interrupt. On return from the interrupt
handler, the softirq ran. We took another interrupt while
running the softirq as we see from the capital 'H'.
wakeup
------
In a Real-Time environment it is very important to know the
wakeup time it takes for the highest priority task that is woken
up to the time that it executes. This is also known as "schedule
latency". I stress the point that this is about RT tasks. It is
also important to know the scheduling latency of non-RT tasks,
but the average schedule latency is better for non-RT tasks.
Tools like LatencyTop are more appropriate for such
measurements.
Real-Time environments are interested in the worst case latency.
That is the longest latency it takes for something to happen,
and not the average. We can have a very fast scheduler that may
only have a large latency once in a while, but that would not
work well with Real-Time tasks. The wakeup tracer was designed
to record the worst case wakeups of RT tasks. Non-RT tasks are
not recorded because the tracer only records one worst case and
tracing non-RT tasks that are unpredictable will overwrite the
worst case latency of RT tasks.
Since this tracer only deals with RT tasks, we will run this
slightly differently than we did with the previous tracers.
Instead of performing an 'ls', we will run 'sleep 1' under
'chrt' which changes the priority of the task.
# echo wakeup > current_tracer
# echo latency-format > trace_options
# echo 0 > tracing_max_latency
# echo 1 > tracing_on
# chrt -f 5 sleep 1
# echo 0 > tracing_on
# cat trace
# tracer: wakeup
#
wakeup latency trace v1.1.5 on 2.6.26-rc8
--------------------------------------------------------------------
latency: 4 us, #2/2, CPU#1 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2)
-----------------
| task: sleep-4901 (uid:0 nice:0 policy:1 rt_prio:5)
-----------------
# _------=> CPU#
# / _-----=> irqs-off
# | / _----=> need-resched
# || / _---=> hardirq/softirq
# ||| / _--=> preempt-depth
# |||| /
# ||||| delay
# cmd pid ||||| time | caller
# \ / ||||| \ | /
<idle>-0 1d.h4 0us+: try_to_wake_up (wake_up_process)
<idle>-0 1d..4 4us : schedule (cpu_idle)
Running this on an idle system, we see that it only took 4
microseconds to perform the task switch. Note, since the trace
marker in the schedule is before the actual "switch", we stop
the tracing when the recorded task is about to schedule in. This
may change if we add a new marker at the end of the scheduler.
Notice that the recorded task is 'sleep' with the PID of 4901
and it has an rt_prio of 5. This priority is user-space priority
and not the internal kernel priority. The policy is 1 for
SCHED_FIFO and 2 for SCHED_RR.
Doing the same with chrt -r 5 and ftrace_enabled set.
# tracer: wakeup
#
wakeup latency trace v1.1.5 on 2.6.26-rc8
--------------------------------------------------------------------
latency: 50 us, #60/60, CPU#1 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2)
-----------------
| task: sleep-4068 (uid:0 nice:0 policy:2 rt_prio:5)
-----------------
# _------=> CPU#
# / _-----=> irqs-off
# | / _----=> need-resched
# || / _---=> hardirq/softirq
# ||| / _--=> preempt-depth
# |||| /
# ||||| delay
# cmd pid ||||| time | caller
# \ / ||||| \ | /
ksoftirq-7 1d.H3 0us : try_to_wake_up (wake_up_process)
ksoftirq-7 1d.H4 1us : sub_preempt_count (marker_probe_cb)
ksoftirq-7 1d.H3 2us : check_preempt_wakeup (try_to_wake_up)
ksoftirq-7 1d.H3 3us : update_curr (check_preempt_wakeup)
ksoftirq-7 1d.H3 4us : calc_delta_mine (update_curr)
ksoftirq-7 1d.H3 5us : __resched_task (check_preempt_wakeup)
ksoftirq-7 1d.H3 6us : task_wake_up_rt (try_to_wake_up)
ksoftirq-7 1d.H3 7us : _spin_unlock_irqrestore (try_to_wake_up)
[...]
ksoftirq-7 1d.H2 17us : irq_exit (smp_apic_timer_interrupt)
ksoftirq-7 1d.H2 18us : sub_preempt_count (irq_exit)
ksoftirq-7 1d.s3 19us : sub_preempt_count (irq_exit)
ksoftirq-7 1..s2 20us : rcu_process_callbacks (__do_softirq)
[...]
ksoftirq-7 1..s2 26us : __rcu_process_callbacks (rcu_process_callbacks)
ksoftirq-7 1d.s2 27us : _local_bh_enable (__do_softirq)
ksoftirq-7 1d.s2 28us : sub_preempt_count (_local_bh_enable)
ksoftirq-7 1.N.3 29us : sub_preempt_count (ksoftirqd)
ksoftirq-7 1.N.2 30us : _cond_resched (ksoftirqd)
ksoftirq-7 1.N.2 31us : __cond_resched (_cond_resched)