aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/asm-generic/vmlinux.lds.h8
-rw-r--r--kernel/trace/Kconfig8
-rw-r--r--scripts/Makefile.build6
-rwxr-xr-xscripts/recordmcount.pl280
4 files changed, 302 insertions, 0 deletions
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 3d8e472a09c8..838d9b2a0da1 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -37,6 +37,13 @@
37#define MEM_DISCARD(sec) *(.mem##sec) 37#define MEM_DISCARD(sec) *(.mem##sec)
38#endif 38#endif
39 39
40#ifdef CONFIG_FTRACE_MCOUNT_RECORD
41#define MCOUNT_REC() VMLINUX_SYMBOL(__start_mcount_loc) = .; \
42 *(__mcount_loc) \
43 VMLINUX_SYMBOL(__stop_mcount_loc) = .;
44#else
45#define MCOUNT_REC()
46#endif
40 47
41/* .data section */ 48/* .data section */
42#define DATA_DATA \ 49#define DATA_DATA \
@@ -192,6 +199,7 @@
192 /* __*init sections */ \ 199 /* __*init sections */ \
193 __init_rodata : AT(ADDR(__init_rodata) - LOAD_OFFSET) { \ 200 __init_rodata : AT(ADDR(__init_rodata) - LOAD_OFFSET) { \
194 *(.ref.rodata) \ 201 *(.ref.rodata) \
202 MCOUNT_REC() \
195 DEV_KEEP(init.rodata) \ 203 DEV_KEEP(init.rodata) \
196 DEV_KEEP(exit.rodata) \ 204 DEV_KEEP(exit.rodata) \
197 CPU_KEEP(init.rodata) \ 205 CPU_KEEP(init.rodata) \
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index cae2637d5e68..14d9505178ca 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -7,6 +7,9 @@ config HAVE_FTRACE
7config HAVE_DYNAMIC_FTRACE 7config HAVE_DYNAMIC_FTRACE
8 bool 8 bool
9 9
10config HAVE_FTRACE_MCOUNT_RECORD
11 bool
12
10config TRACER_MAX_TRACE 13config TRACER_MAX_TRACE
11 bool 14 bool
12 15
@@ -122,6 +125,11 @@ config DYNAMIC_FTRACE
122 were made. If so, it runs stop_machine (stops all CPUS) 125 were made. If so, it runs stop_machine (stops all CPUS)
123 and modifies the code to jump over the call to ftrace. 126 and modifies the code to jump over the call to ftrace.
124 127
128config FTRACE_MCOUNT_RECORD
129 def_bool y
130 depends on DYNAMIC_FTRACE
131 depends on HAVE_FTRACE_MCOUNT_RECORD
132
125config FTRACE_SELFTEST 133config FTRACE_SELFTEST
126 bool 134 bool
127 135
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 277cfe0b7100..463ddcc583ed 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -198,10 +198,16 @@ cmd_modversions = \
198 fi; 198 fi;
199endif 199endif
200 200
201ifdef CONFIG_FTRACE_MCOUNT_RECORD
202cmd_record_mcount = scripts/recordmcount.pl "$(ARCH)" \
203 "$(OBJDUMP)" "$(OBJCOPY)" "$(CC)" "$(LD)" "$(NM)" "$(RM)" "$(MV)" "$(@)";
204endif
205
201define rule_cc_o_c 206define rule_cc_o_c
202 $(call echo-cmd,checksrc) $(cmd_checksrc) \ 207 $(call echo-cmd,checksrc) $(cmd_checksrc) \
203 $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \ 208 $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \
204 $(cmd_modversions) \ 209 $(cmd_modversions) \
210 $(cmd_record_mcount) \
205 scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > \ 211 scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > \
206 $(dot-target).tmp; \ 212 $(dot-target).tmp; \
207 rm -f $(depfile); \ 213 rm -f $(depfile); \
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
new file mode 100755
index 000000000000..44b4b23e91b2
--- /dev/null
+++ b/scripts/recordmcount.pl
@@ -0,0 +1,280 @@
1#!/usr/bin/perl -w
2# (c) 2008, Steven Rostedt <srostedt@redhat.com>
3# Licensed under the terms of the GNU GPL License version 2
4#
5# recordmcount.pl - makes a section called __mcount_loc that holds
6# all the offsets to the calls to mcount.
7#
8#
9# What we want to end up with is a section in vmlinux called
10# __mcount_loc that contains a list of pointers to all the
11# call sites in the kernel that call mcount. Later on boot up, the kernel
12# will read this list, save the locations and turn them into nops.
13# When tracing or profiling is later enabled, these locations will then
14# be converted back to pointers to some function.
15#
16# This is no easy feat. This script is called just after the original
17# object is compiled and before it is linked.
18#
19# The references to the call sites are offsets from the section of text
20# that the call site is in. Hence, all functions in a section that
21# has a call site to mcount, will have the offset from the beginning of
22# the section and not the beginning of the function.
23#
24# The trick is to find a way to record the beginning of the section.
25# The way we do this is to look at the first function in the section
26# which will also be the location of that section after final link.
27# e.g.
28#
29# .section ".text.sched"
30# .globl my_func
31# my_func:
32# [...]
33# call mcount (offset: 0x5)
34# [...]
35# ret
36# other_func:
37# [...]
38# call mcount (offset: 0x1b)
39# [...]
40#
41# Both relocation offsets for the mcounts in the above example will be
42# offset from .text.sched. If we make another file called tmp.s with:
43#
44# .section __mcount_loc
45# .quad my_func + 0x5
46# .quad my_func + 0x1b
47#
48# We can then compile this tmp.s into tmp.o, and link it to the original
49# object.
50#
51# But this gets hard if my_func is not globl (a static function).
52# In such a case we have:
53#
54# .section ".text.sched"
55# my_func:
56# [...]
57# call mcount (offset: 0x5)
58# [...]
59# ret
60# .globl my_func
61# other_func:
62# [...]
63# call mcount (offset: 0x1b)
64# [...]
65#
66# If we make the tmp.s the same as above, when we link together with
67# the original object, we will end up with two symbols for my_func:
68# one local, one global. After final compile, we will end up with
69# an undefined reference to my_func.
70#
71# Since local objects can reference local variables, we need to find
72# a way to make tmp.o reference the local objects of the original object
73# file after it is linked together. To do this, we convert the my_func
74# into a global symbol before linking tmp.o. Then after we link tmp.o
75# we will only have a single symbol for my_func that is global.
76# We can convert my_func back into a local symbol and we are done.
77#
78# Here are the steps we take:
79#
80# 1) Record all the local symbols by using 'nm'
81# 2) Use objdump to find all the call site offsets and sections for
82# mcount.
83# 3) Compile the list into its own object.
84# 4) Do we have to deal with local functions? If not, go to step 8.
85# 5) Make an object that converts these local functions to global symbols
86# with objcopy.
87# 6) Link together this new object with the list object.
88# 7) Convert the local functions back to local symbols and rename
89# the result as the original object.
90# End.
91# 8) Link the object with the list object.
92# 9) Move the result back to the original object.
93# End.
94#
95
96use strict;
97
98my $P = $0;
99$P =~ s@.*/@@g;
100
101my $V = '0.1';
102
103if ($#ARGV < 6) {
104 print "usage: $P arch objdump objcopy cc ld nm rm mv inputfile\n";
105 print "version: $V\n";
106 exit(1);
107}
108
109my ($arch, $objdump, $objcopy, $cc, $ld, $nm, $rm, $mv, $inputfile) = @ARGV;
110
111$objdump = "objdump" if ((length $objdump) == 0);
112$objcopy = "objcopy" if ((length $objcopy) == 0);
113$cc = "gcc" if ((length $cc) == 0);
114$ld = "ld" if ((length $ld) == 0);
115$nm = "nm" if ((length $nm) == 0);
116$rm = "rm" if ((length $rm) == 0);
117$mv = "mv" if ((length $mv) == 0);
118
119#print STDERR "running: $P '$arch' '$objdump' '$objcopy' '$cc' '$ld' " .
120# "'$nm' '$rm' '$mv' '$inputfile'\n";
121
122my %locals;
123my %convert;
124
125my $type;
126my $section_regex; # Find the start of a section
127my $function_regex; # Find the name of a function (return func name)
128my $mcount_regex; # Find the call site to mcount (return offset)
129
130if ($arch eq "x86_64") {
131 $section_regex = "Disassembly of section";
132 $function_regex = "<(.*?)>:";
133 $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount([+-]0x[0-9a-zA-Z]+)?\$";
134 $type = ".quad";
135} elsif ($arch eq "i386") {
136 $section_regex = "Disassembly of section";
137 $function_regex = "<(.*?)>:";
138 $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount\$";
139 $type = ".long";
140} else {
141 die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
142}
143
144my $text_found = 0;
145my $read_function = 0;
146my $opened = 0;
147my $text = "";
148my $mcount_section = "__mcount_loc";
149
150my $dirname;
151my $filename;
152my $prefix;
153my $ext;
154
155if ($inputfile =~ m,^(.*)/([^/]*)$,) {
156 $dirname = $1;
157 $filename = $2;
158} else {
159 $dirname = ".";
160 $filename = $inputfile;
161}
162
163if ($filename =~ m,^(.*)(\.\S),) {
164 $prefix = $1;
165 $ext = $2;
166} else {
167 $prefix = $filename;
168 $ext = "";
169}
170
171my $mcount_s = $dirname . "/.tmp_mc_" . $prefix . ".s";
172my $mcount_o = $dirname . "/.tmp_mc_" . $prefix . ".o";
173
174#
175# Step 1: find all the local symbols (static functions).
176#
177open (IN, "$nm $inputfile|") || die "error running $nm";
178while (<IN>) {
179 if (/^[0-9a-fA-F]+\s+t\s+(\S+)/) {
180 $locals{$1} = 1;
181 }
182}
183close(IN);
184
185#
186# Step 2: find the sections and mcount call sites
187#
188open(IN, "$objdump -dr $inputfile|") || die "error running $objdump";
189
190while (<IN>) {
191 # is it a section?
192 if (/$section_regex/) {
193 $read_function = 1;
194 $text_found = 0;
195 # section found, now is this a start of a function?
196 } elsif ($read_function && /$function_regex/) {
197 $read_function = 0;
198 $text_found = 1;
199 $text = $1;
200 # is this function static? If so, note this fact.
201 if (defined $locals{$text}) {
202 $convert{$text} = 1;
203 }
204 # is this a call site to mcount? If so, print the offset from the section
205 } elsif ($text_found && /$mcount_regex/) {
206 if (!$opened) {
207 open(FILE, ">$mcount_s") || die "can't create $mcount_s\n";
208 $opened = 1;
209 print FILE "\t.section $mcount_section,\"a\",\@progbits\n";
210 }
211 print FILE "\t$type $text + 0x$1\n";
212 }
213}
214
215# If we did not find any mcount callers, we are done (do nothing).
216if (!$opened) {
217 exit(0);
218}
219
220close(FILE);
221
222#
223# Step 3: Compile the file that holds the list of call sites to mcount.
224#
225`$cc -o $mcount_o -c $mcount_s`;
226
227my @converts = keys %convert;
228
229#
230# Step 4: Do we have sections that started with local functions?
231#
232if ($#converts >= 0) {
233 my $globallist = "";
234 my $locallist = "";
235
236 foreach my $con (@converts) {
237 $globallist .= " --globalize-symbol $con";
238 $locallist .= " --localize-symbol $con";
239 }
240
241 my $globalobj = $dirname . "/.tmp_gl_" . $filename;
242 my $globalmix = $dirname . "/.tmp_mx_" . $filename;
243
244 #
245 # Step 5: set up each local function as a global
246 #
247 `$objcopy $globallist $inputfile $globalobj`;
248
249 #
250 # Step 6: Link the global version to our list.
251 #
252 `$ld -r $globalobj $mcount_o -o $globalmix`;
253
254 #
255 # Step 7: Convert the local functions back into local symbols
256 #
257 `$objcopy $locallist $globalmix $inputfile`;
258
259 # Remove the temp files
260 `$rm $globalobj $globalmix`;
261
262} else {
263
264 my $mix = $dirname . "/.tmp_mx_" . $filename;
265
266 #
267 # Step 8: Link the object with our list of call sites object.
268 #
269 `$ld -r $inputfile $mcount_o -o $mix`;
270
271 #
272 # Step 9: Move the result back to the original object.
273 #
274 `$mv $mix $inputfile`;
275}
276
277# Clean up the temp files
278`$rm $mcount_o $mcount_s`;
279
280exit(0);