aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/recordmcount.pl
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2008-08-14 15:45:07 -0400
committerIngo Molnar <mingo@elte.hu>2008-10-14 04:34:40 -0400
commit8da3821ba5634497da63d58a69e24a97697c4a2b (patch)
tree47d4d097b1cd2aafae2e3596a9ae0d1293228782 /scripts/recordmcount.pl
parent8b1fa1d7b22f386747c7b78b918d4c680c16066f (diff)
ftrace: create __mcount_loc section
This patch creates a section in the kernel called "__mcount_loc". This will hold a list of pointers to the mcount relocation for each call site of mcount. For example: objdump -dr init/main.o [...] Disassembly of section .text: 0000000000000000 <do_one_initcall>: 0: 55 push %rbp [...] 000000000000017b <init_post>: 17b: 55 push %rbp 17c: 48 89 e5 mov %rsp,%rbp 17f: 53 push %rbx 180: 48 83 ec 08 sub $0x8,%rsp 184: e8 00 00 00 00 callq 189 <init_post+0xe> 185: R_X86_64_PC32 mcount+0xfffffffffffffffc [...] We will add a section to point to each function call. .section __mcount_loc,"a",@progbits [...] .quad .text + 0x185 [...] The offset to of the mcount call site in init_post is an offset from the start of the section, and not the start of the function init_post. The mcount relocation is at the call site 0x185 from the start of the .text section. .text + 0x185 == init_post + 0xa We need a way to add this __mcount_loc section in a way that we do not lose the relocations after final link. The .text section here will be attached to all other .text sections after final link and the offsets will be meaningless. We need to keep track of where these .text sections are. To do this, we use the start of the first function in the section. do_one_initcall. We can make a tmp.s file with this function as a reference to the start of the .text section. .section __mcount_loc,"a",@progbits [...] .quad do_one_initcall + 0x185 [...] Then we can compile the tmp.s into a tmp.o gcc -c tmp.s -o tmp.o And link it into back into main.o. ld -r main.o tmp.o -o tmp_main.o mv tmp_main.o main.o But we have a problem. What happens if the first function in a section is not exported, and is a static function. The linker will not let the tmp.o use it. This case exists in main.o as well. Disassembly of section .init.text: 0000000000000000 <set_reset_devices>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: e8 00 00 00 00 callq 9 <set_reset_devices+0x9> 5: R_X86_64_PC32 mcount+0xfffffffffffffffc The first function in .init.text is a static function. 00000000000000a8 t __setup_set_reset_devices 000000000000105f t __setup_str_set_reset_devices 0000000000000000 t set_reset_devices The lowercase 't' means that set_reset_devices is local and is not exported. If we simply try to link the tmp.o with the set_reset_devices we end up with two symbols: one local and one global. .section __mcount_loc,"a",@progbits .quad set_reset_devices + 0x10 00000000000000a8 t __setup_set_reset_devices 000000000000105f t __setup_str_set_reset_devices 0000000000000000 t set_reset_devices U set_reset_devices We still have an undefined reference to set_reset_devices, and if we try to compile the kernel, we will end up with an undefined reference to set_reset_devices, or even worst, it could be exported someplace else, and then we will have a reference to the wrong location. To handle this case, we make an intermediate step using objcopy. We convert set_reset_devices into a global exported symbol before linking it with tmp.o and set it back afterwards. 00000000000000a8 t __setup_set_reset_devices 000000000000105f t __setup_str_set_reset_devices 0000000000000000 T set_reset_devices 00000000000000a8 t __setup_set_reset_devices 000000000000105f t __setup_str_set_reset_devices 0000000000000000 T set_reset_devices 00000000000000a8 t __setup_set_reset_devices 000000000000105f t __setup_str_set_reset_devices 0000000000000000 t set_reset_devices Now we have a section in main.o called __mcount_loc that we can place somewhere in the kernel using vmlinux.ld.S and access it to convert all these locations that call mcount into nops before starting SMP and thus, eliminating the need to do this with kstop_machine. Note, A well documented perl script (scripts/recordmcount.pl) is used to do all this in one location. Signed-off-by: Steven Rostedt <srostedt@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'scripts/recordmcount.pl')
-rwxr-xr-xscripts/recordmcount.pl280
1 files changed, 280 insertions, 0 deletions
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);