diff options
author | Steven Rostedt <srostedt@redhat.com> | 2009-11-26 01:34:59 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2009-11-26 01:34:59 -0500 |
commit | fb6da03ff6ed69c9d9a750212cf109f6d5325a72 (patch) | |
tree | 52caecd86009272dc3b9ddefa6e6a009e4e668ab | |
parent | d390b203bff9851385b461a4206d756d5ac1ee60 (diff) |
Make function graph a special override
The function graph tracer is a special case in libparsevent.
It is also the reason for the work arounds in tracecmd with the
callback to trace_read_data() and trace_peek_data().
This patch moves the function graph tracer as a override and out
of the libparsevent.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | parse-events.c | 403 | ||||
-rw-r--r-- | parse-events.h | 8 | ||||
-rw-r--r-- | trace-ftrace.c | 315 |
3 files changed, 315 insertions, 411 deletions
diff --git a/parse-events.c b/parse-events.c index 08f2417..4ae337c 100644 --- a/parse-events.c +++ b/parse-events.c | |||
@@ -2039,8 +2039,8 @@ static int event_read_print(struct event *event) | |||
2039 | return -1; | 2039 | return -1; |
2040 | } | 2040 | } |
2041 | 2041 | ||
2042 | static struct format_field * | 2042 | struct format_field * |
2043 | find_common_field(struct event *event, const char *name) | 2043 | pevent_find_common_field(struct event *event, const char *name) |
2044 | { | 2044 | { |
2045 | struct format_field *format; | 2045 | struct format_field *format; |
2046 | 2046 | ||
@@ -2067,12 +2067,12 @@ pevent_find_field(struct event *event, const char *name) | |||
2067 | return format; | 2067 | return format; |
2068 | } | 2068 | } |
2069 | 2069 | ||
2070 | static struct format_field * | 2070 | struct format_field * |
2071 | find_any_field(struct event *event, const char *name) | 2071 | pevent_find_any_field(struct event *event, const char *name) |
2072 | { | 2072 | { |
2073 | struct format_field *format; | 2073 | struct format_field *format; |
2074 | 2074 | ||
2075 | format = find_common_field(event, name); | 2075 | format = pevent_find_common_field(event, name); |
2076 | if (format) | 2076 | if (format) |
2077 | return format; | 2077 | return format; |
2078 | return pevent_find_field(event, name); | 2078 | return pevent_find_field(event, name); |
@@ -2123,7 +2123,7 @@ static int get_common_info(const char *type, int *offset, int *size) | |||
2123 | die("no event_list!"); | 2123 | die("no event_list!"); |
2124 | 2124 | ||
2125 | event = event_list; | 2125 | event = event_list; |
2126 | field = find_common_field(event, type); | 2126 | field = pevent_find_common_field(event, type); |
2127 | if (!field) | 2127 | if (!field) |
2128 | die("field '%s' not found", type); | 2128 | die("field '%s' not found", type); |
2129 | 2129 | ||
@@ -2196,7 +2196,7 @@ static int parse_common_lock_depth(void *data) | |||
2196 | return ret; | 2196 | return ret; |
2197 | } | 2197 | } |
2198 | 2198 | ||
2199 | static struct event *trace_find_event(int id) | 2199 | struct event *pevent_find_event(int id) |
2200 | { | 2200 | { |
2201 | struct event *event; | 2201 | struct event *event; |
2202 | 2202 | ||
@@ -2207,8 +2207,8 @@ static struct event *trace_find_event(int id) | |||
2207 | return event; | 2207 | return event; |
2208 | } | 2208 | } |
2209 | 2209 | ||
2210 | static struct event * | 2210 | struct event * |
2211 | trace_find_event_by_name(const char *sys, const char *name) | 2211 | pevent_find_event_by_name(const char *sys, const char *name) |
2212 | { | 2212 | { |
2213 | struct event *event; | 2213 | struct event *event; |
2214 | 2214 | ||
@@ -2240,7 +2240,7 @@ static unsigned long long eval_num_arg(void *data, int size, | |||
2240 | return strtoull(arg->atom.atom, NULL, 0); | 2240 | return strtoull(arg->atom.atom, NULL, 0); |
2241 | case PRINT_FIELD: | 2241 | case PRINT_FIELD: |
2242 | if (!arg->field.field) { | 2242 | if (!arg->field.field) { |
2243 | arg->field.field = find_any_field(event, arg->field.name); | 2243 | arg->field.field = pevent_find_any_field(event, arg->field.name); |
2244 | if (!arg->field.field) | 2244 | if (!arg->field.field) |
2245 | die("field %s not found", arg->field.name); | 2245 | die("field %s not found", arg->field.name); |
2246 | } | 2246 | } |
@@ -2288,7 +2288,7 @@ static unsigned long long eval_num_arg(void *data, int size, | |||
2288 | case PRINT_FIELD: | 2288 | case PRINT_FIELD: |
2289 | if (!larg->field.field) { | 2289 | if (!larg->field.field) { |
2290 | larg->field.field = | 2290 | larg->field.field = |
2291 | find_any_field(event, larg->field.name); | 2291 | pevent_find_any_field(event, larg->field.name); |
2292 | if (!larg->field.field) | 2292 | if (!larg->field.field) |
2293 | die("field %s not found", larg->field.name); | 2293 | die("field %s not found", larg->field.name); |
2294 | } | 2294 | } |
@@ -2429,7 +2429,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, | |||
2429 | return; | 2429 | return; |
2430 | case PRINT_FIELD: | 2430 | case PRINT_FIELD: |
2431 | if (!arg->field.field) { | 2431 | if (!arg->field.field) { |
2432 | arg->field.field = find_any_field(event, arg->field.name); | 2432 | arg->field.field = pevent_find_any_field(event, arg->field.name); |
2433 | if (!arg->field.field) | 2433 | if (!arg->field.field) |
2434 | die("field %s not found", arg->field.name); | 2434 | die("field %s not found", arg->field.name); |
2435 | } | 2435 | } |
@@ -2490,7 +2490,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, | |||
2490 | if (arg->string.offset == -1) { | 2490 | if (arg->string.offset == -1) { |
2491 | struct format_field *f; | 2491 | struct format_field *f; |
2492 | 2492 | ||
2493 | f = find_any_field(event, arg->string.string); | 2493 | f = pevent_find_any_field(event, arg->string.string); |
2494 | arg->string.offset = f->offset; | 2494 | arg->string.offset = f->offset; |
2495 | } | 2495 | } |
2496 | str_offset = *(int *)(data + arg->string.offset); | 2496 | str_offset = *(int *)(data + arg->string.offset); |
@@ -2763,7 +2763,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event | |||
2763 | fmt = "%.2x%.2x%.2x%.2x%.2x%.2x"; | 2763 | fmt = "%.2x%.2x%.2x%.2x%.2x%.2x"; |
2764 | if (!arg->field.field) { | 2764 | if (!arg->field.field) { |
2765 | arg->field.field = | 2765 | arg->field.field = |
2766 | find_any_field(event, arg->field.name); | 2766 | pevent_find_any_field(event, arg->field.name); |
2767 | if (!arg->field.field) | 2767 | if (!arg->field.field) |
2768 | die("field %s not found", arg->field.name); | 2768 | die("field %s not found", arg->field.name); |
2769 | } | 2769 | } |
@@ -2845,15 +2845,6 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event | |||
2845 | } | 2845 | } |
2846 | } | 2846 | } |
2847 | 2847 | ||
2848 | static inline int log10_cpu(int nb) | ||
2849 | { | ||
2850 | if (nb / 100) | ||
2851 | return 3; | ||
2852 | if (nb / 10) | ||
2853 | return 2; | ||
2854 | return 1; | ||
2855 | } | ||
2856 | |||
2857 | static void print_lat_fmt(struct trace_seq *s, void *data, int size __unused) | 2848 | static void print_lat_fmt(struct trace_seq *s, void *data, int size __unused) |
2858 | { | 2849 | { |
2859 | unsigned int lat_flags; | 2850 | unsigned int lat_flags; |
@@ -2889,354 +2880,6 @@ static void print_lat_fmt(struct trace_seq *s, void *data, int size __unused) | |||
2889 | trace_seq_printf(s, "%d", lock_depth); | 2880 | trace_seq_printf(s, "%d", lock_depth); |
2890 | } | 2881 | } |
2891 | 2882 | ||
2892 | /* taken from Linux, written by Frederic Weisbecker */ | ||
2893 | static void print_graph_cpu(struct trace_seq *s, int cpu) | ||
2894 | { | ||
2895 | int i; | ||
2896 | int log10_this = log10_cpu(cpu); | ||
2897 | int log10_all = log10_cpu(cpus); | ||
2898 | |||
2899 | |||
2900 | /* | ||
2901 | * Start with a space character - to make it stand out | ||
2902 | * to the right a bit when trace output is pasted into | ||
2903 | * email: | ||
2904 | */ | ||
2905 | trace_seq_putc(s, ' '); | ||
2906 | |||
2907 | /* | ||
2908 | * Tricky - we space the CPU field according to the max | ||
2909 | * number of online CPUs. On a 2-cpu system it would take | ||
2910 | * a maximum of 1 digit - on a 128 cpu system it would | ||
2911 | * take up to 3 digits: | ||
2912 | */ | ||
2913 | for (i = 0; i < log10_all - log10_this; i++) | ||
2914 | trace_seq_putc(s, ' '); | ||
2915 | |||
2916 | trace_seq_printf(s, "%d) ", cpu); | ||
2917 | } | ||
2918 | |||
2919 | #define TRACE_GRAPH_PROCINFO_LENGTH 14 | ||
2920 | #define TRACE_GRAPH_INDENT 2 | ||
2921 | |||
2922 | static void print_graph_proc(struct trace_seq *s, int pid, const char *comm) | ||
2923 | { | ||
2924 | /* sign + log10(MAX_INT) + '\0' */ | ||
2925 | char pid_str[11]; | ||
2926 | int spaces = 0; | ||
2927 | int len; | ||
2928 | int i; | ||
2929 | |||
2930 | sprintf(pid_str, "%d", pid); | ||
2931 | |||
2932 | /* 1 stands for the "-" character */ | ||
2933 | len = strlen(comm) + strlen(pid_str) + 1; | ||
2934 | |||
2935 | if (len < TRACE_GRAPH_PROCINFO_LENGTH) | ||
2936 | spaces = TRACE_GRAPH_PROCINFO_LENGTH - len; | ||
2937 | |||
2938 | /* First spaces to align center */ | ||
2939 | for (i = 0; i < spaces / 2; i++) | ||
2940 | trace_seq_putc(s, ' '); | ||
2941 | |||
2942 | trace_seq_printf(s, "%s-%s", comm, pid_str); | ||
2943 | |||
2944 | /* Last spaces to align center */ | ||
2945 | for (i = 0; i < spaces - (spaces / 2); i++) | ||
2946 | trace_seq_putc(s, ' '); | ||
2947 | } | ||
2948 | |||
2949 | static struct record * | ||
2950 | get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, | ||
2951 | struct record *next) | ||
2952 | { | ||
2953 | struct format_field *field; | ||
2954 | struct event *event; | ||
2955 | unsigned long val; | ||
2956 | int type; | ||
2957 | int pid; | ||
2958 | |||
2959 | type = trace_parse_common_type(next->data); | ||
2960 | event = trace_find_event(type); | ||
2961 | if (!event) | ||
2962 | return NULL; | ||
2963 | |||
2964 | if (!(event->flags & EVENT_FL_ISFUNCRET)) | ||
2965 | return NULL; | ||
2966 | |||
2967 | pid = parse_common_pid(next->data); | ||
2968 | field = pevent_find_field(event, "func"); | ||
2969 | if (!field) | ||
2970 | die("function return does not have field func"); | ||
2971 | |||
2972 | val = pevent_read_number(next->data + field->offset, field->size); | ||
2973 | |||
2974 | if (cur_pid != pid || cur_func != val) | ||
2975 | return NULL; | ||
2976 | |||
2977 | /* this is a leaf, now advance the iterator */ | ||
2978 | return trace_read_data(cpu); | ||
2979 | } | ||
2980 | |||
2981 | /* Signal a overhead of time execution to the output */ | ||
2982 | static void print_graph_overhead(struct trace_seq *s, | ||
2983 | unsigned long long duration) | ||
2984 | { | ||
2985 | /* Non nested entry or return */ | ||
2986 | if (duration == ~0ULL) | ||
2987 | return (void)trace_seq_printf(s, " "); | ||
2988 | |||
2989 | /* Duration exceeded 100 msecs */ | ||
2990 | if (duration > 100000ULL) | ||
2991 | return (void)trace_seq_printf(s, "! "); | ||
2992 | |||
2993 | /* Duration exceeded 10 msecs */ | ||
2994 | if (duration > 10000ULL) | ||
2995 | return (void)trace_seq_printf(s, "+ "); | ||
2996 | |||
2997 | trace_seq_printf(s, " "); | ||
2998 | } | ||
2999 | |||
3000 | static void print_graph_duration(struct trace_seq *s, unsigned long long duration) | ||
3001 | { | ||
3002 | unsigned long usecs = duration / 1000; | ||
3003 | unsigned long nsecs_rem = duration % 1000; | ||
3004 | /* log10(ULONG_MAX) + '\0' */ | ||
3005 | char msecs_str[21]; | ||
3006 | char nsecs_str[5]; | ||
3007 | int len; | ||
3008 | int i; | ||
3009 | |||
3010 | sprintf(msecs_str, "%lu", usecs); | ||
3011 | |||
3012 | /* Print msecs */ | ||
3013 | len = s->len; | ||
3014 | trace_seq_printf(s, "%lu", usecs); | ||
3015 | |||
3016 | /* Print nsecs (we don't want to exceed 7 numbers) */ | ||
3017 | if ((s->len - len) < 7) { | ||
3018 | snprintf(nsecs_str, 8 - (s->len - len), "%03lu", nsecs_rem); | ||
3019 | trace_seq_printf(s, ".%s", nsecs_str); | ||
3020 | } | ||
3021 | |||
3022 | len = s->len - len; | ||
3023 | |||
3024 | trace_seq_puts(s, " us "); | ||
3025 | |||
3026 | /* Print remaining spaces to fit the row's width */ | ||
3027 | for (i = len; i < 7; i++) | ||
3028 | trace_seq_putc(s, ' '); | ||
3029 | |||
3030 | trace_seq_puts(s, "| "); | ||
3031 | } | ||
3032 | |||
3033 | static void | ||
3034 | print_graph_entry_leaf(struct trace_seq *s, | ||
3035 | struct event *event, void *data, struct record *ret_rec) | ||
3036 | { | ||
3037 | unsigned long long rettime, calltime; | ||
3038 | unsigned long long duration, depth; | ||
3039 | unsigned long long val; | ||
3040 | struct format_field *field; | ||
3041 | struct func_map *func; | ||
3042 | struct event *ret_event; | ||
3043 | int type; | ||
3044 | int i; | ||
3045 | |||
3046 | type = trace_parse_common_type(ret_rec->data); | ||
3047 | ret_event = trace_find_event(type); | ||
3048 | |||
3049 | field = pevent_find_field(ret_event, "rettime"); | ||
3050 | if (!field) | ||
3051 | die("can't find rettime in return graph"); | ||
3052 | rettime = pevent_read_number(ret_rec->data + field->offset, field->size); | ||
3053 | |||
3054 | field = pevent_find_field(ret_event, "calltime"); | ||
3055 | if (!field) | ||
3056 | die("can't find rettime in return graph"); | ||
3057 | calltime = pevent_read_number(ret_rec->data + field->offset, field->size); | ||
3058 | |||
3059 | duration = rettime - calltime; | ||
3060 | |||
3061 | /* Overhead */ | ||
3062 | print_graph_overhead(s, duration); | ||
3063 | |||
3064 | /* Duration */ | ||
3065 | print_graph_duration(s, duration); | ||
3066 | |||
3067 | field = pevent_find_field(event, "depth"); | ||
3068 | if (!field) | ||
3069 | die("can't find depth in entry graph"); | ||
3070 | depth = pevent_read_number(data + field->offset, field->size); | ||
3071 | |||
3072 | /* Function */ | ||
3073 | for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) | ||
3074 | trace_seq_putc(s, ' '); | ||
3075 | |||
3076 | field = pevent_find_field(event, "func"); | ||
3077 | if (!field) | ||
3078 | die("can't find func in entry graph"); | ||
3079 | val = pevent_read_number(data + field->offset, field->size); | ||
3080 | func = find_func(val); | ||
3081 | |||
3082 | if (func) | ||
3083 | trace_seq_printf(s, "%s();", func->func); | ||
3084 | else | ||
3085 | trace_seq_printf(s, "%llx();", val); | ||
3086 | } | ||
3087 | |||
3088 | static void print_graph_nested(struct trace_seq *s, | ||
3089 | struct event *event, void *data) | ||
3090 | { | ||
3091 | struct format_field *field; | ||
3092 | unsigned long long depth; | ||
3093 | unsigned long long val; | ||
3094 | struct func_map *func; | ||
3095 | int i; | ||
3096 | |||
3097 | /* No overhead */ | ||
3098 | print_graph_overhead(s, -1); | ||
3099 | |||
3100 | /* No time */ | ||
3101 | trace_seq_puts(s, " | "); | ||
3102 | |||
3103 | field = pevent_find_field(event, "depth"); | ||
3104 | if (!field) | ||
3105 | die("can't find depth in entry graph"); | ||
3106 | depth = pevent_read_number(data + field->offset, field->size); | ||
3107 | |||
3108 | /* Function */ | ||
3109 | for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) | ||
3110 | trace_seq_putc(s, ' '); | ||
3111 | |||
3112 | field = pevent_find_field(event, "func"); | ||
3113 | if (!field) | ||
3114 | die("can't find func in entry graph"); | ||
3115 | val = pevent_read_number(data + field->offset, field->size); | ||
3116 | func = find_func(val); | ||
3117 | |||
3118 | if (func) | ||
3119 | trace_seq_printf(s, "%s() {", func->func); | ||
3120 | else | ||
3121 | trace_seq_printf(s, "%llx() {", val); | ||
3122 | } | ||
3123 | |||
3124 | static void | ||
3125 | pretty_print_func_ent(struct trace_seq *s, | ||
3126 | void *data, int size, struct event *event, | ||
3127 | int cpu, int pid, const char *comm, | ||
3128 | unsigned long secs, unsigned long usecs) | ||
3129 | { | ||
3130 | struct format_field *field; | ||
3131 | struct record *rec; | ||
3132 | void *copy_data; | ||
3133 | unsigned long val; | ||
3134 | |||
3135 | trace_seq_printf(s, "%5lu.%06lu | ", secs, usecs); | ||
3136 | |||
3137 | print_graph_cpu(s, cpu); | ||
3138 | print_graph_proc(s, pid, comm); | ||
3139 | |||
3140 | trace_seq_puts(s, " | "); | ||
3141 | |||
3142 | if (latency_format) { | ||
3143 | print_lat_fmt(s, data, size); | ||
3144 | trace_seq_puts(s, " | "); | ||
3145 | } | ||
3146 | |||
3147 | field = pevent_find_field(event, "func"); | ||
3148 | if (!field) | ||
3149 | die("function entry does not have func field"); | ||
3150 | |||
3151 | val = pevent_read_number(data + field->offset, field->size); | ||
3152 | |||
3153 | /* | ||
3154 | * peek_data may unmap the data pointer. Copy it first. | ||
3155 | */ | ||
3156 | copy_data = malloc_or_die(size); | ||
3157 | memcpy(copy_data, data, size); | ||
3158 | data = copy_data; | ||
3159 | |||
3160 | rec = trace_peek_data(cpu); | ||
3161 | if (rec) { | ||
3162 | rec = get_return_for_leaf(cpu, pid, val, rec); | ||
3163 | if (rec) { | ||
3164 | print_graph_entry_leaf(s, event, data, rec); | ||
3165 | goto out_free; | ||
3166 | } | ||
3167 | } | ||
3168 | print_graph_nested(s, event, data); | ||
3169 | out_free: | ||
3170 | free(data); | ||
3171 | } | ||
3172 | |||
3173 | static void | ||
3174 | pretty_print_func_ret(struct trace_seq *s, | ||
3175 | void *data, int size __unused, struct event *event, | ||
3176 | int cpu, int pid, const char *comm, | ||
3177 | unsigned long secs, unsigned long usecs) | ||
3178 | { | ||
3179 | unsigned long long rettime, calltime; | ||
3180 | unsigned long long duration, depth; | ||
3181 | struct format_field *field; | ||
3182 | int i; | ||
3183 | |||
3184 | trace_seq_printf(s, "%5lu.%06lu | ", secs, usecs); | ||
3185 | |||
3186 | print_graph_cpu(s, cpu); | ||
3187 | print_graph_proc(s, pid, comm); | ||
3188 | |||
3189 | trace_seq_puts(s, " | "); | ||
3190 | |||
3191 | if (latency_format) { | ||
3192 | print_lat_fmt(s, data, size); | ||
3193 | trace_seq_puts(s, " | "); | ||
3194 | } | ||
3195 | |||
3196 | field = pevent_find_field(event, "rettime"); | ||
3197 | if (!field) | ||
3198 | die("can't find rettime in return graph"); | ||
3199 | rettime = pevent_read_number(data + field->offset, field->size); | ||
3200 | |||
3201 | field = pevent_find_field(event, "calltime"); | ||
3202 | if (!field) | ||
3203 | die("can't find calltime in return graph"); | ||
3204 | calltime = pevent_read_number(data + field->offset, field->size); | ||
3205 | |||
3206 | duration = rettime - calltime; | ||
3207 | |||
3208 | /* Overhead */ | ||
3209 | print_graph_overhead(s, duration); | ||
3210 | |||
3211 | /* Duration */ | ||
3212 | print_graph_duration(s, duration); | ||
3213 | |||
3214 | field = pevent_find_field(event, "depth"); | ||
3215 | if (!field) | ||
3216 | die("can't find depth in entry graph"); | ||
3217 | depth = pevent_read_number(data + field->offset, field->size); | ||
3218 | |||
3219 | /* Function */ | ||
3220 | for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) | ||
3221 | trace_seq_putc(s, ' '); | ||
3222 | |||
3223 | trace_seq_putc(s, '}'); | ||
3224 | } | ||
3225 | |||
3226 | static void | ||
3227 | pretty_print_func_graph(struct trace_seq *s, | ||
3228 | void *data, int size, struct event *event, | ||
3229 | int cpu, int pid, const char *comm, | ||
3230 | unsigned long secs, unsigned long usecs) | ||
3231 | { | ||
3232 | if (event->flags & EVENT_FL_ISFUNCENT) | ||
3233 | pretty_print_func_ent(s, data, size, event, | ||
3234 | cpu, pid, comm, secs, usecs); | ||
3235 | else if (event->flags & EVENT_FL_ISFUNCRET) | ||
3236 | pretty_print_func_ret(s, data, size, event, | ||
3237 | cpu, pid, comm, secs, usecs); | ||
3238 | } | ||
3239 | |||
3240 | void pevent_print_event(struct trace_seq *s, | 2883 | void pevent_print_event(struct trace_seq *s, |
3241 | int cpu, void *data, int size, unsigned long long nsecs) | 2884 | int cpu, void *data, int size, unsigned long long nsecs) |
3242 | { | 2885 | { |
@@ -3253,7 +2896,7 @@ void pevent_print_event(struct trace_seq *s, | |||
3253 | 2896 | ||
3254 | type = trace_parse_common_type(data); | 2897 | type = trace_parse_common_type(data); |
3255 | 2898 | ||
3256 | event = trace_find_event(type); | 2899 | event = pevent_find_event(type); |
3257 | if (!event) { | 2900 | if (!event) { |
3258 | warning("ug! no event found for type %d", type); | 2901 | warning("ug! no event found for type %d", type); |
3259 | return; | 2902 | return; |
@@ -3262,10 +2905,6 @@ void pevent_print_event(struct trace_seq *s, | |||
3262 | pid = parse_common_pid(data); | 2905 | pid = parse_common_pid(data); |
3263 | comm = find_cmdline(pid); | 2906 | comm = find_cmdline(pid); |
3264 | 2907 | ||
3265 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) | ||
3266 | return pretty_print_func_graph(s, data, size, event, cpu, | ||
3267 | pid, comm, secs, usecs); | ||
3268 | |||
3269 | if (latency_format) { | 2908 | if (latency_format) { |
3270 | trace_seq_printf(s, "%8.8s-%-5d %3d", | 2909 | trace_seq_printf(s, "%8.8s-%-5d %3d", |
3271 | comm, pid, cpu); | 2910 | comm, pid, cpu); |
@@ -3464,13 +3103,7 @@ int pevent_parse_event(char *buf, unsigned long size, char *sys) | |||
3464 | 3103 | ||
3465 | event->flags |= EVENT_FL_ISFTRACE; | 3104 | event->flags |= EVENT_FL_ISFTRACE; |
3466 | 3105 | ||
3467 | if (strcmp(event->name, "funcgraph_entry") == 0) | 3106 | if (strcmp(event->name, "bprint") == 0) |
3468 | event->flags |= EVENT_FL_ISFUNCENT; | ||
3469 | |||
3470 | else if (strcmp(event->name, "funcgraph_exit") == 0) | ||
3471 | event->flags |= EVENT_FL_ISFUNCRET; | ||
3472 | |||
3473 | else if (strcmp(event->name, "bprint") == 0) | ||
3474 | event->flags |= EVENT_FL_ISBPRINT; | 3107 | event->flags |= EVENT_FL_ISBPRINT; |
3475 | } | 3108 | } |
3476 | 3109 | ||
@@ -3533,7 +3166,7 @@ int pevent_register_event_handler(int id, char *sys_name, char *event_name, | |||
3533 | 3166 | ||
3534 | if (id >= 0) { | 3167 | if (id >= 0) { |
3535 | /* search by id */ | 3168 | /* search by id */ |
3536 | event = trace_find_event(id); | 3169 | event = pevent_find_event(id); |
3537 | if (!event) | 3170 | if (!event) |
3538 | return -1; | 3171 | return -1; |
3539 | if (event_name && (strcmp(event_name, event->name) != 0)) | 3172 | if (event_name && (strcmp(event_name, event->name) != 0)) |
@@ -3541,7 +3174,7 @@ int pevent_register_event_handler(int id, char *sys_name, char *event_name, | |||
3541 | if (sys_name && (strcmp(sys_name, event->system) != 0)) | 3174 | if (sys_name && (strcmp(sys_name, event->system) != 0)) |
3542 | return -1; | 3175 | return -1; |
3543 | } else { | 3176 | } else { |
3544 | event = trace_find_event_by_name(sys_name, event_name); | 3177 | event = pevent_find_event_by_name(sys_name, event_name); |
3545 | if (!event) | 3178 | if (!event) |
3546 | return -1; | 3179 | return -1; |
3547 | } | 3180 | } |
diff --git a/parse-events.h b/parse-events.h index 20c8aec..074adb1 100644 --- a/parse-events.h +++ b/parse-events.h | |||
@@ -301,12 +301,20 @@ int pevent_parse_event(char *buf, unsigned long size, char *sys); | |||
301 | int pevent_register_event_handler(int id, char *sys_name, char *event_name, | 301 | int pevent_register_event_handler(int id, char *sys_name, char *event_name, |
302 | pevent_event_handler_func func); | 302 | pevent_event_handler_func func); |
303 | 303 | ||
304 | struct format_field *pevent_find_common_field(struct event *event, const char *name); | ||
304 | struct format_field *pevent_find_field(struct event *event, const char *name); | 305 | struct format_field *pevent_find_field(struct event *event, const char *name); |
306 | struct format_field *pevent_find_any_field(struct event *event, const char *name); | ||
307 | |||
305 | const char *pevent_find_function(unsigned long long addr); | 308 | const char *pevent_find_function(unsigned long long addr); |
306 | unsigned long long pevent_read_number(const void *ptr, int size); | 309 | unsigned long long pevent_read_number(const void *ptr, int size); |
307 | int pevent_read_number_field(struct format_field *field, const void *data, | 310 | int pevent_read_number_field(struct format_field *field, const void *data, |
308 | unsigned long long *value); | 311 | unsigned long long *value); |
309 | 312 | ||
313 | struct event *pevent_find_event(int id); | ||
314 | |||
315 | struct event * | ||
316 | pevent_find_event_by_name(const char *sys, const char *name); | ||
317 | |||
310 | 318 | ||
311 | /* for debugging */ | 319 | /* for debugging */ |
312 | void pevent_print_funcs(void); | 320 | void pevent_print_funcs(void); |
diff --git a/trace-ftrace.c b/trace-ftrace.c index 07de014..16b012e 100644 --- a/trace-ftrace.c +++ b/trace-ftrace.c | |||
@@ -4,48 +4,311 @@ | |||
4 | 4 | ||
5 | #include "parse-events.h" | 5 | #include "parse-events.h" |
6 | 6 | ||
7 | static struct event *fgraph_ret_event; | ||
8 | static int fgraph_ret_id; | ||
9 | |||
10 | static int get_field_val(struct trace_seq *s, void *data, | ||
11 | struct event *event, const char *name, | ||
12 | unsigned long long *val) | ||
13 | { | ||
14 | struct format_field *field; | ||
15 | |||
16 | field = pevent_find_any_field(event, name); | ||
17 | if (!field) { | ||
18 | trace_seq_printf(s, "<CANT FIND FIELD %s>", name); | ||
19 | return -1; | ||
20 | } | ||
21 | |||
22 | if (pevent_read_number_field(field, data, val)) { | ||
23 | trace_seq_printf(s, " %s=INVALID", name); | ||
24 | return -1; | ||
25 | } | ||
26 | |||
27 | return 0; | ||
28 | } | ||
29 | |||
7 | static int function_handler(struct trace_seq *s, void *data, int size, | 30 | static int function_handler(struct trace_seq *s, void *data, int size, |
8 | struct event *event, int cpu, | 31 | struct event *event, int cpu, |
9 | unsigned long long nsecs) | 32 | unsigned long long nsecs) |
10 | { | 33 | { |
11 | struct format_field *ip = pevent_find_field(event, "ip"); | ||
12 | struct format_field *pip = pevent_find_field(event, "parent_ip"); | ||
13 | unsigned long long function; | 34 | unsigned long long function; |
14 | const char *func; | 35 | const char *func; |
15 | 36 | ||
16 | if (!ip) | 37 | if (get_field_val(s, data, event, "ip", &function)) |
17 | return trace_seq_printf(s, "CANT FIND FIELD IP"); | 38 | return trace_seq_putc(s, '!'); |
18 | |||
19 | if (pevent_read_number_field(ip, data, &function)) | ||
20 | trace_seq_printf(s, " function=INVALID"); | ||
21 | else { | ||
22 | func = pevent_find_function(function); | ||
23 | if (func) | ||
24 | trace_seq_printf(s, "%s <-- ", func); | ||
25 | else | ||
26 | trace_seq_printf(s, "0x%llx", function); | ||
27 | } | ||
28 | 39 | ||
29 | if (!pip) | 40 | func = pevent_find_function(function); |
30 | return trace_seq_printf(s, "CANT FIND FIELD PARENT_IP"); | 41 | if (func) |
31 | 42 | trace_seq_printf(s, "%s <-- ", func); | |
32 | if (pevent_read_number_field(pip, data, &function)) | 43 | else |
33 | trace_seq_printf(s, " function=INVALID"); | 44 | trace_seq_printf(s, "0x%llx", function); |
34 | else { | 45 | |
35 | func = pevent_find_function(function); | 46 | if (get_field_val(s, data, event, "parent_ip", &function)) |
36 | if (func) | 47 | return trace_seq_putc(s, '!'); |
37 | trace_seq_printf(s, "%s", func); | 48 | |
38 | else | 49 | func = pevent_find_function(function); |
39 | trace_seq_printf(s, "0x%llx", function); | 50 | if (func) |
40 | } | 51 | trace_seq_printf(s, "%s", func); |
52 | else | ||
53 | trace_seq_printf(s, "0x%llx", function); | ||
41 | 54 | ||
42 | return 0; | 55 | return 0; |
43 | } | 56 | } |
44 | 57 | ||
58 | #define TRACE_GRAPH_INDENT 2 | ||
59 | |||
60 | static struct record * | ||
61 | get_return_for_leaf(struct trace_seq *s, int cpu, int cur_pid, | ||
62 | unsigned long long cur_func, struct record *next) | ||
63 | { | ||
64 | unsigned long long val; | ||
65 | unsigned long long type; | ||
66 | unsigned long long pid; | ||
67 | |||
68 | /* Searching a common field, can use any event */ | ||
69 | if (get_field_val(s, next->data, fgraph_ret_event, "common_type", &type)) | ||
70 | return NULL; | ||
71 | |||
72 | if (type != fgraph_ret_id) | ||
73 | return NULL; | ||
74 | |||
75 | if (get_field_val(s, next->data, fgraph_ret_event, "common_pid", &pid)) | ||
76 | return NULL; | ||
77 | |||
78 | if (cur_pid != pid) | ||
79 | return NULL; | ||
80 | |||
81 | /* We aleady know this is a funcgraph_ret_event */ | ||
82 | if (get_field_val(s, next->data, fgraph_ret_event, "func", &val)) | ||
83 | return NULL; | ||
84 | |||
85 | if (cur_func != val) | ||
86 | return NULL; | ||
87 | |||
88 | /* this is a leaf, now advance the iterator */ | ||
89 | return trace_read_data(cpu); | ||
90 | } | ||
91 | |||
92 | /* Signal a overhead of time execution to the output */ | ||
93 | static void print_graph_overhead(struct trace_seq *s, | ||
94 | unsigned long long duration) | ||
95 | { | ||
96 | /* Non nested entry or return */ | ||
97 | if (duration == ~0ULL) | ||
98 | return (void)trace_seq_printf(s, " "); | ||
99 | |||
100 | /* Duration exceeded 100 msecs */ | ||
101 | if (duration > 100000ULL) | ||
102 | return (void)trace_seq_printf(s, "! "); | ||
103 | |||
104 | /* Duration exceeded 10 msecs */ | ||
105 | if (duration > 10000ULL) | ||
106 | return (void)trace_seq_printf(s, "+ "); | ||
107 | |||
108 | trace_seq_printf(s, " "); | ||
109 | } | ||
110 | |||
111 | static void print_graph_duration(struct trace_seq *s, unsigned long long duration) | ||
112 | { | ||
113 | unsigned long usecs = duration / 1000; | ||
114 | unsigned long nsecs_rem = duration % 1000; | ||
115 | /* log10(ULONG_MAX) + '\0' */ | ||
116 | char msecs_str[21]; | ||
117 | char nsecs_str[5]; | ||
118 | int len; | ||
119 | int i; | ||
120 | |||
121 | sprintf(msecs_str, "%lu", usecs); | ||
122 | |||
123 | /* Print msecs */ | ||
124 | len = s->len; | ||
125 | trace_seq_printf(s, "%lu", usecs); | ||
126 | |||
127 | /* Print nsecs (we don't want to exceed 7 numbers) */ | ||
128 | if ((s->len - len) < 7) { | ||
129 | snprintf(nsecs_str, 8 - (s->len - len), "%03lu", nsecs_rem); | ||
130 | trace_seq_printf(s, ".%s", nsecs_str); | ||
131 | } | ||
132 | |||
133 | len = s->len - len; | ||
134 | |||
135 | trace_seq_puts(s, " us "); | ||
136 | |||
137 | /* Print remaining spaces to fit the row's width */ | ||
138 | for (i = len; i < 7; i++) | ||
139 | trace_seq_putc(s, ' '); | ||
140 | |||
141 | trace_seq_puts(s, "| "); | ||
142 | } | ||
143 | |||
144 | static int | ||
145 | print_graph_entry_leaf(struct trace_seq *s, | ||
146 | struct event *event, void *data, struct record *ret_rec) | ||
147 | { | ||
148 | unsigned long long rettime, calltime; | ||
149 | unsigned long long duration, depth; | ||
150 | unsigned long long val; | ||
151 | const char *func; | ||
152 | int i; | ||
153 | |||
154 | |||
155 | if (get_field_val(s, ret_rec->data, fgraph_ret_event, "rettime", &rettime)) | ||
156 | return trace_seq_putc(s, '!'); | ||
157 | |||
158 | if (get_field_val(s, ret_rec->data, fgraph_ret_event, "calltime", &calltime)) | ||
159 | return trace_seq_putc(s, '!'); | ||
160 | |||
161 | duration = rettime - calltime; | ||
162 | |||
163 | /* Overhead */ | ||
164 | print_graph_overhead(s, duration); | ||
165 | |||
166 | /* Duration */ | ||
167 | print_graph_duration(s, duration); | ||
168 | |||
169 | if (get_field_val(s, data, event, "depth", &depth)) | ||
170 | return trace_seq_putc(s, '!'); | ||
171 | |||
172 | /* Function */ | ||
173 | for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) | ||
174 | trace_seq_putc(s, ' '); | ||
175 | |||
176 | if (get_field_val(s, data, event, "func", &val)) | ||
177 | return trace_seq_putc(s, '!'); | ||
178 | func = pevent_find_function(val); | ||
179 | |||
180 | if (func) | ||
181 | return trace_seq_printf(s, "%s();", func); | ||
182 | else | ||
183 | return trace_seq_printf(s, "%llx();", val); | ||
184 | } | ||
185 | |||
186 | static int print_graph_nested(struct trace_seq *s, | ||
187 | struct event *event, void *data) | ||
188 | { | ||
189 | unsigned long long depth; | ||
190 | unsigned long long val; | ||
191 | const char *func; | ||
192 | int i; | ||
193 | |||
194 | /* No overhead */ | ||
195 | print_graph_overhead(s, -1); | ||
196 | |||
197 | /* No time */ | ||
198 | trace_seq_puts(s, " | "); | ||
199 | |||
200 | if (get_field_val(s, data, event, "depth", &depth)) | ||
201 | return trace_seq_putc(s, '!'); | ||
202 | |||
203 | /* Function */ | ||
204 | for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) | ||
205 | trace_seq_putc(s, ' '); | ||
206 | |||
207 | if (get_field_val(s, data, event, "func", &val)) | ||
208 | return trace_seq_putc(s, '!'); | ||
209 | |||
210 | func = pevent_find_function(val); | ||
211 | |||
212 | if (func) | ||
213 | return trace_seq_printf(s, "%s() {", func); | ||
214 | else | ||
215 | return trace_seq_printf(s, "%llx() {", val); | ||
216 | } | ||
217 | |||
218 | static int | ||
219 | fgraph_ent_handler(struct trace_seq *s, void *data, int size, | ||
220 | struct event *event, int cpu, | ||
221 | unsigned long long nsecs) | ||
222 | { | ||
223 | struct record *rec; | ||
224 | void *copy_data; | ||
225 | unsigned long long val, pid; | ||
226 | int ret; | ||
227 | |||
228 | if (get_field_val(s, data, event, "common_pid", &pid)) | ||
229 | return trace_seq_putc(s, '!'); | ||
230 | |||
231 | if (get_field_val(s, data, event, "func", &val)) | ||
232 | return trace_seq_putc(s, '!'); | ||
233 | |||
234 | /* | ||
235 | * peek_data may unmap the data pointer. Copy it first. | ||
236 | */ | ||
237 | copy_data = malloc(size); | ||
238 | if (!copy_data) | ||
239 | return trace_seq_printf(s, " <FAILED TO ALLOCATE MEMORY!>"); | ||
240 | |||
241 | memcpy(copy_data, data, size); | ||
242 | data = copy_data; | ||
243 | |||
244 | rec = trace_peek_data(cpu); | ||
245 | if (rec) | ||
246 | rec = get_return_for_leaf(s, cpu, pid, val, rec); | ||
247 | if (rec) | ||
248 | ret = print_graph_entry_leaf(s, event, data, rec); | ||
249 | else | ||
250 | ret = print_graph_nested(s, event, data); | ||
251 | |||
252 | free(data); | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | static int | ||
257 | fgraph_ret_handler(struct trace_seq *s, void *data, int size, | ||
258 | struct event *event, int cpu, | ||
259 | unsigned long long nsecs) | ||
260 | { | ||
261 | unsigned long long rettime, calltime; | ||
262 | unsigned long long duration, depth; | ||
263 | int i; | ||
264 | |||
265 | /* Compensate that exit is one char less than entry */ | ||
266 | trace_seq_putc(s, ' '); | ||
267 | |||
268 | if (get_field_val(s, data, event, "rettime", &rettime)) | ||
269 | return trace_seq_putc(s, '!'); | ||
270 | |||
271 | if (get_field_val(s, data, event, "calltime", &calltime)) | ||
272 | return trace_seq_putc(s, '!'); | ||
273 | |||
274 | duration = rettime - calltime; | ||
275 | |||
276 | /* Overhead */ | ||
277 | print_graph_overhead(s, duration); | ||
278 | |||
279 | /* Duration */ | ||
280 | print_graph_duration(s, duration); | ||
281 | |||
282 | if (get_field_val(s, data, event, "depth", &depth)) | ||
283 | return trace_seq_putc(s, '!'); | ||
284 | |||
285 | /* Function */ | ||
286 | for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) | ||
287 | trace_seq_putc(s, ' '); | ||
288 | |||
289 | return trace_seq_putc(s, '}'); | ||
290 | } | ||
291 | |||
45 | int tracecmd_ftrace_overrides(void) | 292 | int tracecmd_ftrace_overrides(void) |
46 | { | 293 | { |
294 | struct event *event; | ||
295 | |||
47 | pevent_register_event_handler(-1, "ftrace", "function", | 296 | pevent_register_event_handler(-1, "ftrace", "function", |
48 | function_handler); | 297 | function_handler); |
49 | 298 | ||
299 | pevent_register_event_handler(-1, "ftrace", "funcgraph_entry", | ||
300 | fgraph_ent_handler); | ||
301 | |||
302 | pevent_register_event_handler(-1, "ftrace", "funcgraph_exit", | ||
303 | fgraph_ret_handler); | ||
304 | |||
305 | /* Store the func ret id and event for later use */ | ||
306 | event = pevent_find_event_by_name("ftrace", "funcgraph_exit"); | ||
307 | if (!event) | ||
308 | return 0; | ||
309 | |||
310 | fgraph_ret_id = event->id; | ||
311 | fgraph_ret_event = event; | ||
312 | |||
50 | return 0; | 313 | return 0; |
51 | } | 314 | } |