13
13
#include <signal.h>
14
14
#include <stdlib.h>
15
15
#include <fcntl.h>
16
+ #include <math.h>
16
17
#include <poll.h>
18
+ #include <ctype.h>
17
19
#include <linux/capability.h>
18
20
#include <linux/string.h>
19
21
@@ -702,6 +704,224 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace)
702
704
return (done && !workload_exec_errno ) ? 0 : -1 ;
703
705
}
704
706
707
+ #define NUM_BUCKET 22 /* 20 + 2 (for outliers in both direction) */
708
+
709
+ static void make_histogram (int buckets [], char * buf , size_t len , char * linebuf )
710
+ {
711
+ char * p , * q ;
712
+ char * unit ;
713
+ double num ;
714
+ int i ;
715
+
716
+ /* ensure NUL termination */
717
+ buf [len ] = '\0' ;
718
+
719
+ /* handle data line by line */
720
+ for (p = buf ; (q = strchr (p , '\n' )) != NULL ; p = q + 1 ) {
721
+ * q = '\0' ;
722
+ /* move it to the line buffer */
723
+ strcat (linebuf , p );
724
+
725
+ /*
726
+ * parse trace output to get function duration like in
727
+ *
728
+ * # tracer: function_graph
729
+ * #
730
+ * # CPU DURATION FUNCTION CALLS
731
+ * # | | | | | | |
732
+ * 1) + 10.291 us | do_filp_open();
733
+ * 1) 4.889 us | do_filp_open();
734
+ * 1) 6.086 us | do_filp_open();
735
+ *
736
+ */
737
+ if (linebuf [0 ] == '#' )
738
+ goto next ;
739
+
740
+ /* ignore CPU */
741
+ p = strchr (linebuf , ')' );
742
+ if (p == NULL )
743
+ p = linebuf ;
744
+
745
+ while (* p && !isdigit (* p ) && (* p != '|' ))
746
+ p ++ ;
747
+
748
+ /* no duration */
749
+ if (* p == '\0' || * p == '|' )
750
+ goto next ;
751
+
752
+ num = strtod (p , & unit );
753
+ if (!unit || strncmp (unit , " us" , 3 ))
754
+ goto next ;
755
+
756
+ i = log2 (num );
757
+ if (i < 0 )
758
+ i = 0 ;
759
+ if (i >= NUM_BUCKET )
760
+ i = NUM_BUCKET - 1 ;
761
+
762
+ buckets [i ]++ ;
763
+
764
+ next :
765
+ /* empty the line buffer for the next output */
766
+ linebuf [0 ] = '\0' ;
767
+ }
768
+
769
+ /* preserve any remaining output (before newline) */
770
+ strcat (linebuf , p );
771
+ }
772
+
773
+ static void display_histogram (int buckets [])
774
+ {
775
+ int i ;
776
+ int total = 0 ;
777
+ int bar_total = 46 ; /* to fit in 80 column */
778
+ char bar [] = "###############################################" ;
779
+ int bar_len ;
780
+
781
+ for (i = 0 ; i < NUM_BUCKET ; i ++ )
782
+ total += buckets [i ];
783
+
784
+ if (total == 0 ) {
785
+ printf ("No data found\n" );
786
+ return ;
787
+ }
788
+
789
+ printf ("# %14s | %10s | %-*s |\n" ,
790
+ " DURATION " , "COUNT" , bar_total , "GRAPH" );
791
+
792
+ bar_len = buckets [0 ] * bar_total / total ;
793
+ printf (" %4d - %-4d %s | %10d | %.*s%*s |\n" ,
794
+ 0 , 1 , "us" , buckets [0 ], bar_len , bar , bar_total - bar_len , "" );
795
+
796
+ for (i = 1 ; i < NUM_BUCKET - 1 ; i ++ ) {
797
+ int start = (1 << (i - 1 ));
798
+ int stop = 1 << i ;
799
+ const char * unit = "us" ;
800
+
801
+ if (start >= 1024 ) {
802
+ start >>= 10 ;
803
+ stop >>= 10 ;
804
+ unit = "ms" ;
805
+ }
806
+ bar_len = buckets [i ] * bar_total / total ;
807
+ printf (" %4d - %-4d %s | %10d | %.*s%*s |\n" ,
808
+ start , stop , unit , buckets [i ], bar_len , bar ,
809
+ bar_total - bar_len , "" );
810
+ }
811
+
812
+ bar_len = buckets [NUM_BUCKET - 1 ] * bar_total / total ;
813
+ printf (" %4d - %-4s %s | %10d | %.*s%*s |\n" ,
814
+ 1 , "..." , " s" , buckets [NUM_BUCKET - 1 ], bar_len , bar ,
815
+ bar_total - bar_len , "" );
816
+
817
+ }
818
+
819
+ static int __cmd_latency (struct perf_ftrace * ftrace )
820
+ {
821
+ char * trace_file ;
822
+ int trace_fd ;
823
+ char buf [4096 ];
824
+ char line [256 ];
825
+ struct pollfd pollfd = {
826
+ .events = POLLIN ,
827
+ };
828
+ int buckets [NUM_BUCKET ] = { };
829
+
830
+ if (!(perf_cap__capable (CAP_PERFMON ) ||
831
+ perf_cap__capable (CAP_SYS_ADMIN ))) {
832
+ pr_err ("ftrace only works for %s!\n" ,
833
+ #ifdef HAVE_LIBCAP_SUPPORT
834
+ "users with the CAP_PERFMON or CAP_SYS_ADMIN capability"
835
+ #else
836
+ "root"
837
+ #endif
838
+ );
839
+ return -1 ;
840
+ }
841
+
842
+ if (reset_tracing_files (ftrace ) < 0 ) {
843
+ pr_err ("failed to reset ftrace\n" );
844
+ goto out ;
845
+ }
846
+
847
+ /* reset ftrace buffer */
848
+ if (write_tracing_file ("trace" , "0" ) < 0 )
849
+ goto out ;
850
+
851
+ if (set_tracing_options (ftrace ) < 0 )
852
+ goto out_reset ;
853
+
854
+ /* force to use the function_graph tracer to track duration */
855
+ if (write_tracing_file ("current_tracer" , "function_graph" ) < 0 ) {
856
+ pr_err ("failed to set current_tracer to function_graph\n" );
857
+ goto out_reset ;
858
+ }
859
+
860
+ trace_file = get_tracing_file ("trace_pipe" );
861
+ if (!trace_file ) {
862
+ pr_err ("failed to open trace_pipe\n" );
863
+ goto out_reset ;
864
+ }
865
+
866
+ trace_fd = open (trace_file , O_RDONLY );
867
+
868
+ put_tracing_file (trace_file );
869
+
870
+ if (trace_fd < 0 ) {
871
+ pr_err ("failed to open trace_pipe\n" );
872
+ goto out_reset ;
873
+ }
874
+
875
+ fcntl (trace_fd , F_SETFL , O_NONBLOCK );
876
+ pollfd .fd = trace_fd ;
877
+
878
+ if (write_tracing_file ("tracing_on" , "1" ) < 0 ) {
879
+ pr_err ("can't enable tracing\n" );
880
+ goto out_close_fd ;
881
+ }
882
+
883
+ evlist__start_workload (ftrace -> evlist );
884
+
885
+ line [0 ] = '\0' ;
886
+ while (!done ) {
887
+ if (poll (& pollfd , 1 , -1 ) < 0 )
888
+ break ;
889
+
890
+ if (pollfd .revents & POLLIN ) {
891
+ int n = read (trace_fd , buf , sizeof (buf ) - 1 );
892
+ if (n < 0 )
893
+ break ;
894
+
895
+ make_histogram (buckets , buf , n , line );
896
+ }
897
+ }
898
+
899
+ write_tracing_file ("tracing_on" , "0" );
900
+
901
+ if (workload_exec_errno ) {
902
+ const char * emsg = str_error_r (workload_exec_errno , buf , sizeof (buf ));
903
+ pr_err ("workload failed: %s\n" , emsg );
904
+ goto out_close_fd ;
905
+ }
906
+
907
+ /* read remaining buffer contents */
908
+ while (true) {
909
+ int n = read (trace_fd , buf , sizeof (buf ) - 1 );
910
+ if (n <= 0 )
911
+ break ;
912
+ make_histogram (buckets , buf , n , line );
913
+ }
914
+
915
+ display_histogram (buckets );
916
+
917
+ out_close_fd :
918
+ close (trace_fd );
919
+ out_reset :
920
+ reset_tracing_files (ftrace );
921
+ out :
922
+ return (done && !workload_exec_errno ) ? 0 : -1 ;
923
+ }
924
+
705
925
static int perf_ftrace_config (const char * var , const char * value , void * cb )
706
926
{
707
927
struct perf_ftrace * ftrace = cb ;
@@ -864,6 +1084,12 @@ static int parse_graph_tracer_opts(const struct option *opt,
864
1084
return 0 ;
865
1085
}
866
1086
1087
+ enum perf_ftrace_subcommand {
1088
+ PERF_FTRACE_NONE ,
1089
+ PERF_FTRACE_TRACE ,
1090
+ PERF_FTRACE_LATENCY ,
1091
+ };
1092
+
867
1093
int cmd_ftrace (int argc , const char * * argv )
868
1094
{
869
1095
int ret ;
@@ -915,14 +1141,21 @@ int cmd_ftrace(int argc, const char **argv)
915
1141
"Number of milliseconds to wait before starting tracing after program start" ),
916
1142
OPT_PARENT (common_options ),
917
1143
};
1144
+ const struct option latency_options [] = {
1145
+ OPT_CALLBACK ('T' , "trace-funcs" , & ftrace .filters , "func" ,
1146
+ "Show latency of given function" , parse_filter_func ),
1147
+ OPT_PARENT (common_options ),
1148
+ };
1149
+ const struct option * options = ftrace_options ;
918
1150
919
1151
const char * const ftrace_usage [] = {
920
1152
"perf ftrace [<options>] [<command>]" ,
921
1153
"perf ftrace [<options>] -- [<command>] [<options>]" ,
922
- "perf ftrace trace [<options>] [<command>]" ,
923
- "perf ftrace trace [<options>] -- [<command>] [<options>]" ,
1154
+ "perf ftrace { trace|latency} [<options>] [<command>]" ,
1155
+ "perf ftrace { trace|latency} [<options>] -- [<command>] [<options>]" ,
924
1156
NULL
925
1157
};
1158
+ enum perf_ftrace_subcommand subcmd = PERF_FTRACE_NONE ;
926
1159
927
1160
INIT_LIST_HEAD (& ftrace .filters );
928
1161
INIT_LIST_HEAD (& ftrace .notrace );
@@ -938,15 +1171,29 @@ int cmd_ftrace(int argc, const char **argv)
938
1171
if (ret < 0 )
939
1172
return -1 ;
940
1173
941
- if (argc > 1 && !strcmp (argv [1 ], "trace" )) {
942
- argc -- ;
943
- argv ++ ;
1174
+ if (argc > 1 ) {
1175
+ if (!strcmp (argv [1 ], "trace" )) {
1176
+ subcmd = PERF_FTRACE_TRACE ;
1177
+ } else if (!strcmp (argv [1 ], "latency" )) {
1178
+ subcmd = PERF_FTRACE_LATENCY ;
1179
+ options = latency_options ;
1180
+ }
1181
+
1182
+ if (subcmd != PERF_FTRACE_NONE ) {
1183
+ argc -- ;
1184
+ argv ++ ;
1185
+ }
944
1186
}
1187
+ /* for backward compatibility */
1188
+ if (subcmd == PERF_FTRACE_NONE )
1189
+ subcmd = PERF_FTRACE_TRACE ;
945
1190
946
- argc = parse_options (argc , argv , ftrace_options , ftrace_usage ,
1191
+ argc = parse_options (argc , argv , options , ftrace_usage ,
947
1192
PARSE_OPT_STOP_AT_NON_OPTION );
948
- if (!argc && target__none (& ftrace .target ))
949
- ftrace .target .system_wide = true;
1193
+ if (argc < 0 ) {
1194
+ ret = - EINVAL ;
1195
+ goto out_delete_filters ;
1196
+ }
950
1197
951
1198
ret = target__validate (& ftrace .target );
952
1199
if (ret ) {
@@ -975,7 +1222,27 @@ int cmd_ftrace(int argc, const char **argv)
975
1222
goto out_delete_evlist ;
976
1223
}
977
1224
978
- ret = __cmd_ftrace (& ftrace );
1225
+ switch (subcmd ) {
1226
+ case PERF_FTRACE_TRACE :
1227
+ if (!argc && target__none (& ftrace .target ))
1228
+ ftrace .target .system_wide = true;
1229
+ ret = __cmd_ftrace (& ftrace );
1230
+ break ;
1231
+ case PERF_FTRACE_LATENCY :
1232
+ if (list_empty (& ftrace .filters )) {
1233
+ pr_err ("Should provide a function to measure\n" );
1234
+ parse_options_usage (ftrace_usage , options , "T" , 1 );
1235
+ ret = - EINVAL ;
1236
+ goto out_delete_evlist ;
1237
+ }
1238
+ ret = __cmd_latency (& ftrace );
1239
+ break ;
1240
+ case PERF_FTRACE_NONE :
1241
+ default :
1242
+ pr_err ("Invalid subcommand\n" );
1243
+ ret = - EINVAL ;
1244
+ break ;
1245
+ }
979
1246
980
1247
out_delete_evlist :
981
1248
evlist__delete (ftrace .evlist );
0 commit comments