From 8da3821ba5634497da63d58a69e24a97697c4a2b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 14 Aug 2008 15:45:07 -0400 Subject: 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 : 0: 55 push %rbp [...] 000000000000017b : 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 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 : 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: e8 00 00 00 00 callq 9 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 Signed-off-by: Ingo Molnar --- scripts/Makefile.build | 6 ++ scripts/recordmcount.pl | 280 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100755 scripts/recordmcount.pl (limited to 'scripts') 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 = \ fi; endif +ifdef CONFIG_FTRACE_MCOUNT_RECORD +cmd_record_mcount = scripts/recordmcount.pl "$(ARCH)" \ + "$(OBJDUMP)" "$(OBJCOPY)" "$(CC)" "$(LD)" "$(NM)" "$(RM)" "$(MV)" "$(@)"; +endif + define rule_cc_o_c $(call echo-cmd,checksrc) $(cmd_checksrc) \ $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); \ $(cmd_modversions) \ + $(cmd_record_mcount) \ scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > \ $(dot-target).tmp; \ 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 @@ +#!/usr/bin/perl -w +# (c) 2008, Steven Rostedt +# Licensed under the terms of the GNU GPL License version 2 +# +# recordmcount.pl - makes a section called __mcount_loc that holds +# all the offsets to the calls to mcount. +# +# +# What we want to end up with is a section in vmlinux called +# __mcount_loc that contains a list of pointers to all the +# call sites in the kernel that call mcount. Later on boot up, the kernel +# will read this list, save the locations and turn them into nops. +# When tracing or profiling is later enabled, these locations will then +# be converted back to pointers to some function. +# +# This is no easy feat. This script is called just after the original +# object is compiled and before it is linked. +# +# The references to the call sites are offsets from the section of text +# that the call site is in. Hence, all functions in a section that +# has a call site to mcount, will have the offset from the beginning of +# the section and not the beginning of the function. +# +# The trick is to find a way to record the beginning of the section. +# The way we do this is to look at the first function in the section +# which will also be the location of that section after final link. +# e.g. +# +# .section ".text.sched" +# .globl my_func +# my_func: +# [...] +# call mcount (offset: 0x5) +# [...] +# ret +# other_func: +# [...] +# call mcount (offset: 0x1b) +# [...] +# +# Both relocation offsets for the mcounts in the above example will be +# offset from .text.sched. If we make another file called tmp.s with: +# +# .section __mcount_loc +# .quad my_func + 0x5 +# .quad my_func + 0x1b +# +# We can then compile this tmp.s into tmp.o, and link it to the original +# object. +# +# But this gets hard if my_func is not globl (a static function). +# In such a case we have: +# +# .section ".text.sched" +# my_func: +# [...] +# call mcount (offset: 0x5) +# [...] +# ret +# .globl my_func +# other_func: +# [...] +# call mcount (offset: 0x1b) +# [...] +# +# If we make the tmp.s the same as above, when we link together with +# the original object, we will end up with two symbols for my_func: +# one local, one global. After final compile, we will end up with +# an undefined reference to my_func. +# +# Since local objects can reference local variables, we need to find +# a way to make tmp.o reference the local objects of the original object +# file after it is linked together. To do this, we convert the my_func +# into a global symbol before linking tmp.o. Then after we link tmp.o +# we will only have a single symbol for my_func that is global. +# We can convert my_func back into a local symbol and we are done. +# +# Here are the steps we take: +# +# 1) Record all the local symbols by using 'nm' +# 2) Use objdump to find all the call site offsets and sections for +# mcount. +# 3) Compile the list into its own object. +# 4) Do we have to deal with local functions? If not, go to step 8. +# 5) Make an object that converts these local functions to global symbols +# with objcopy. +# 6) Link together this new object with the list object. +# 7) Convert the local functions back to local symbols and rename +# the result as the original object. +# End. +# 8) Link the object with the list object. +# 9) Move the result back to the original object. +# End. +# + +use strict; + +my $P = $0; +$P =~ s@.*/@@g; + +my $V = '0.1'; + +if ($#ARGV < 6) { + print "usage: $P arch objdump objcopy cc ld nm rm mv inputfile\n"; + print "version: $V\n"; + exit(1); +} + +my ($arch, $objdump, $objcopy, $cc, $ld, $nm, $rm, $mv, $inputfile) = @ARGV; + +$objdump = "objdump" if ((length $objdump) == 0); +$objcopy = "objcopy" if ((length $objcopy) == 0); +$cc = "gcc" if ((length $cc) == 0); +$ld = "ld" if ((length $ld) == 0); +$nm = "nm" if ((length $nm) == 0); +$rm = "rm" if ((length $rm) == 0); +$mv = "mv" if ((length $mv) == 0); + +#print STDERR "running: $P '$arch' '$objdump' '$objcopy' '$cc' '$ld' " . +# "'$nm' '$rm' '$mv' '$inputfile'\n"; + +my %locals; +my %convert; + +my $type; +my $section_regex; # Find the start of a section +my $function_regex; # Find the name of a function (return func name) +my $mcount_regex; # Find the call site to mcount (return offset) + +if ($arch eq "x86_64") { + $section_regex = "Disassembly of section"; + $function_regex = "<(.*?)>:"; + $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount([+-]0x[0-9a-zA-Z]+)?\$"; + $type = ".quad"; +} elsif ($arch eq "i386") { + $section_regex = "Disassembly of section"; + $function_regex = "<(.*?)>:"; + $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount\$"; + $type = ".long"; +} else { + die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD"; +} + +my $text_found = 0; +my $read_function = 0; +my $opened = 0; +my $text = ""; +my $mcount_section = "__mcount_loc"; + +my $dirname; +my $filename; +my $prefix; +my $ext; + +if ($inputfile =~ m,^(.*)/([^/]*)$,) { + $dirname = $1; + $filename = $2; +} else { + $dirname = "."; + $filename = $inputfile; +} + +if ($filename =~ m,^(.*)(\.\S),) { + $prefix = $1; + $ext = $2; +} else { + $prefix = $filename; + $ext = ""; +} + +my $mcount_s = $dirname . "/.tmp_mc_" . $prefix . ".s"; +my $mcount_o = $dirname . "/.tmp_mc_" . $prefix . ".o"; + +# +# Step 1: find all the local symbols (static functions). +# +open (IN, "$nm $inputfile|") || die "error running $nm"; +while () { + if (/^[0-9a-fA-F]+\s+t\s+(\S+)/) { + $locals{$1} = 1; + } +} +close(IN); + +# +# Step 2: find the sections and mcount call sites +# +open(IN, "$objdump -dr $inputfile|") || die "error running $objdump"; + +while () { + # is it a section? + if (/$section_regex/) { + $read_function = 1; + $text_found = 0; + # section found, now is this a start of a function? + } elsif ($read_function && /$function_regex/) { + $read_function = 0; + $text_found = 1; + $text = $1; + # is this function static? If so, note this fact. + if (defined $locals{$text}) { + $convert{$text} = 1; + } + # is this a call site to mcount? If so, print the offset from the section + } elsif ($text_found && /$mcount_regex/) { + if (!$opened) { + open(FILE, ">$mcount_s") || die "can't create $mcount_s\n"; + $opened = 1; + print FILE "\t.section $mcount_section,\"a\",\@progbits\n"; + } + print FILE "\t$type $text + 0x$1\n"; + } +} + +# If we did not find any mcount callers, we are done (do nothing). +if (!$opened) { + exit(0); +} + +close(FILE); + +# +# Step 3: Compile the file that holds the list of call sites to mcount. +# +`$cc -o $mcount_o -c $mcount_s`; + +my @converts = keys %convert; + +# +# Step 4: Do we have sections that started with local functions? +# +if ($#converts >= 0) { + my $globallist = ""; + my $locallist = ""; + + foreach my $con (@converts) { + $globallist .= " --globalize-symbol $con"; + $locallist .= " --localize-symbol $con"; + } + + my $globalobj = $dirname . "/.tmp_gl_" . $filename; + my $globalmix = $dirname . "/.tmp_mx_" . $filename; + + # + # Step 5: set up each local function as a global + # + `$objcopy $globallist $inputfile $globalobj`; + + # + # Step 6: Link the global version to our list. + # + `$ld -r $globalobj $mcount_o -o $globalmix`; + + # + # Step 7: Convert the local functions back into local symbols + # + `$objcopy $locallist $globalmix $inputfile`; + + # Remove the temp files + `$rm $globalobj $globalmix`; + +} else { + + my $mix = $dirname . "/.tmp_mx_" . $filename; + + # + # Step 8: Link the object with our list of call sites object. + # + `$ld -r $inputfile $mcount_o -o $mix`; + + # + # Step 9: Move the result back to the original object. + # + `$mv $mix $inputfile`; +} + +# Clean up the temp files +`$rm $mcount_o $mcount_s`; + +exit(0); -- cgit v1.2.3 From 3989cce82b272bd6312555fcc47c11715c157102 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 9 Jun 2008 20:54:22 +0200 Subject: ftrace: scripts/recordmcount.pl cross-build hack hack around: ld: Relocatable linking with relocations from format elf32-i386 (init/.tmp_gl_calibrate.o) to format elf64-x86-64 (init/.tmp_mx_calibrate.o) i CC arch/x86/mm/extable.o objcopy: 'init/.tmp_mx_calibrate.o': No such file rm: cannot remove `init/.tmp_mx_calibrate.o': No such file or directory ld: Relocatable linking with relocations from format elf32-i386 (arch/x86/mm/extable.o) to format elf64-x86-64 (arch/x86/mm/.tmp_mx_extable.o) is not supported mv: cannot stat `arch/x86/mm/.tmp_mx_extable.o': No such file or directory ld: Relocatable linking with relocations from format elf32-i386 (arch/x86/mm/fault.o) to format elf64-x86-64 (arch/x86/mm/.tmp_mx_fault.o) is not supported Signed-off-by: Ingo Molnar --- scripts/recordmcount.pl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'scripts') diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 44b4b23e91b2..e4922a6c963b 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -108,6 +108,20 @@ if ($#ARGV < 6) { my ($arch, $objdump, $objcopy, $cc, $ld, $nm, $rm, $mv, $inputfile) = @ARGV; +if ($arch eq "i386") { + $ld = "ld -m elf_i386"; + $objdump = "objdump -M i386"; + $objcopy = "objcopy -O elf32-i386"; + $cc = "gcc -m32"; +} + +if ($arch eq "x86_64") { + $ld = "ld -m elf_x86_64"; + $objdump = "objdump -M x86-64"; + $objcopy = "objcopy -O elf64-x86-64"; + $cc = "gcc -m64"; +} + $objdump = "objdump" if ((length $objdump) == 0); $objcopy = "objcopy" if ((length $objcopy) == 0); $cc = "gcc" if ((length $cc) == 0); -- cgit v1.2.3 From 6a4917e3ae5194a10e0723f96edc854c381e3063 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 18 Aug 2008 15:58:12 -0700 Subject: ftrace: fix build problem with CONFIG_FTRACE I'm seeing when I use separate src/build dirs: make[3]: *** [arch/x86/kernel/time_32.o] Error 1 /bin/sh: scripts/recordmcount.pl: No such file or directory make[3]: *** [arch/x86/kernel/irq_32.o] Error 1 /bin/sh: scripts/recordmcount.pl: No such file or directory make[3]: *** [arch/x86/kernel/ldt.o] Error 1 /bin/sh: scripts/recordmcount.pl: No such file or directory make[3]: *** [arch/x86/kernel/i8259.o] Error 1 /bin/sh: scripts/recordmcount.pl: No such file or directory This fixes it. Signed-off-by: Ingo Molnar --- scripts/Makefile.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 463ddcc583ed..232485ec5265 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -199,7 +199,7 @@ cmd_modversions = \ endif ifdef CONFIG_FTRACE_MCOUNT_RECORD -cmd_record_mcount = scripts/recordmcount.pl "$(ARCH)" \ +cmd_record_mcount = $(srctree)/scripts/recordmcount.pl "$(ARCH)" \ "$(OBJDUMP)" "$(OBJCOPY)" "$(CC)" "$(LD)" "$(NM)" "$(RM)" "$(MV)" "$(@)"; endif -- cgit v1.2.3 From d74fcd1e4e8842d5302cd303ef25cef7e67f68b4 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 15 Aug 2008 11:40:24 -0400 Subject: ftrace: update recordmount.pl arch changes I'm trying to keep all the arch changes in recordmcount.pl in one place. I moved your code into that area, by adding the flags to the commands that were passed in. Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar --- scripts/recordmcount.pl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index e4922a6c963b..36c8355c555e 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -108,20 +108,6 @@ if ($#ARGV < 6) { my ($arch, $objdump, $objcopy, $cc, $ld, $nm, $rm, $mv, $inputfile) = @ARGV; -if ($arch eq "i386") { - $ld = "ld -m elf_i386"; - $objdump = "objdump -M i386"; - $objcopy = "objcopy -O elf32-i386"; - $cc = "gcc -m32"; -} - -if ($arch eq "x86_64") { - $ld = "ld -m elf_x86_64"; - $objdump = "objdump -M x86-64"; - $objcopy = "objcopy -O elf64-x86-64"; - $cc = "gcc -m64"; -} - $objdump = "objdump" if ((length $objdump) == 0); $objcopy = "objcopy" if ((length $objcopy) == 0); $cc = "gcc" if ((length $cc) == 0); @@ -146,11 +132,25 @@ if ($arch eq "x86_64") { $function_regex = "<(.*?)>:"; $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount([+-]0x[0-9a-zA-Z]+)?\$"; $type = ".quad"; + + # force flags for this arch + $ld .= " -m elf_x86_64"; + $objdump .= " -M x86-64"; + $objcopy .= " -O elf64-x86-64"; + $cc .= " -m64"; + } elsif ($arch eq "i386") { $section_regex = "Disassembly of section"; $function_regex = "<(.*?)>:"; $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount\$"; $type = ".long"; + + # force flags for this arch + $ld .= " -m elf_i386"; + $objdump .= " -M i386"; + $objcopy .= " -O elf32-i386"; + $cc .= " -m32"; + } else { die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD"; } -- cgit v1.2.3 From 8feff1cacc29e9cfdc6d1ce5f2108db87b91046e Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 20 Aug 2008 10:07:35 -0400 Subject: ftrace: handle weak symbol functions During tests and checks, I've discovered that there were failures to convert mcount callers into nops. Looking deeper into these failures, code that was attempted to be changed was not an mcount caller. The current code only updates if the code being changed is what it expects, but I still investigate any time there is a failure. What was happening is that a weak symbol was being used as a reference for other mcount callers. That weak symbol was also referenced elsewhere so the offsets were using the strong symbol and not the function symbol that it was referenced from. This patch changes the setting up of the mcount_loc section to search for a global function that is not weak. It will pick a local over a weak but if only a weak is found in a section, a warning is printed and the mcount location is not recorded (just to be safe). Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar --- scripts/recordmcount.pl | 106 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 20 deletions(-) (limited to 'scripts') diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 36c8355c555e..1891cf9743fc 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -119,17 +119,19 @@ $mv = "mv" if ((length $mv) == 0); #print STDERR "running: $P '$arch' '$objdump' '$objcopy' '$cc' '$ld' " . # "'$nm' '$rm' '$mv' '$inputfile'\n"; -my %locals; -my %convert; +my %locals; # List of local (static) functions +my %weak; # List of weak functions +my %convert; # List of local functions used that needs conversion my $type; my $section_regex; # Find the start of a section -my $function_regex; # Find the name of a function (return func name) +my $function_regex; # Find the name of a function + # (return offset and func name) my $mcount_regex; # Find the call site to mcount (return offset) if ($arch eq "x86_64") { $section_regex = "Disassembly of section"; - $function_regex = "<(.*?)>:"; + $function_regex = "^([0-9a-fA-F]+)\\s+<(.*?)>:"; $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount([+-]0x[0-9a-zA-Z]+)?\$"; $type = ".quad"; @@ -141,7 +143,7 @@ if ($arch eq "x86_64") { } elsif ($arch eq "i386") { $section_regex = "Disassembly of section"; - $function_regex = "<(.*?)>:"; + $function_regex = "^([0-9a-fA-F]+)\\s+<(.*?)>:"; $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\smcount\$"; $type = ".long"; @@ -158,7 +160,6 @@ if ($arch eq "x86_64") { my $text_found = 0; my $read_function = 0; my $opened = 0; -my $text = ""; my $mcount_section = "__mcount_loc"; my $dirname; @@ -186,46 +187,111 @@ my $mcount_s = $dirname . "/.tmp_mc_" . $prefix . ".s"; my $mcount_o = $dirname . "/.tmp_mc_" . $prefix . ".o"; # -# Step 1: find all the local symbols (static functions). +# Step 1: find all the local (static functions) and weak symbols. +# 't' is local, 'w/W' is weak (we never use a weak function) # open (IN, "$nm $inputfile|") || die "error running $nm"; while () { if (/^[0-9a-fA-F]+\s+t\s+(\S+)/) { $locals{$1} = 1; + } elsif (/^[0-9a-fA-F]+\s+([wW])\s+(\S+)/) { + $weak{$2} = $1; } } close(IN); +my @offsets; # Array of offsets of mcount callers +my $ref_func; # reference function to use for offsets +my $offset = 0; # offset of ref_func to section beginning + +## +# update_funcs - print out the current mcount callers +# +# Go through the list of offsets to callers and write them to +# the output file in a format that can be read by an assembler. +# +sub update_funcs +{ + return if ($#offsets < 0); + + defined($ref_func) || die "No function to reference"; + + # A section only had a weak function, to represent it. + # Unfortunately, a weak function may be overwritten by another + # function of the same name, making all these offsets incorrect. + # To be safe, we simply print a warning and bail. + if (defined $weak{$ref_func}) { + print STDERR + "$inputfile: WARNING: referencing weak function" . + " $ref_func for mcount\n"; + return; + } + + # is this function static? If so, note this fact. + if (defined $locals{$ref_func}) { + $convert{$ref_func} = 1; + } + + # Loop through all the mcount caller offsets and print a reference + # to the caller based from the ref_func. + for (my $i=0; $i <= $#offsets; $i++) { + if (!$opened) { + open(FILE, ">$mcount_s") || die "can't create $mcount_s\n"; + $opened = 1; + print FILE "\t.section $mcount_section,\"a\",\@progbits\n"; + } + printf FILE "\t%s %s + %d\n", $type, $ref_func, $offsets[$i] - $offset; + } +} + # # Step 2: find the sections and mcount call sites # open(IN, "$objdump -dr $inputfile|") || die "error running $objdump"; +my $text; + while () { # is it a section? if (/$section_regex/) { $read_function = 1; + # print out any recorded offsets + update_funcs() if ($text_found); + + # reset all markers and arrays $text_found = 0; + undef($ref_func); + undef(@offsets); + # section found, now is this a start of a function? } elsif ($read_function && /$function_regex/) { - $read_function = 0; $text_found = 1; - $text = $1; - # is this function static? If so, note this fact. - if (defined $locals{$text}) { - $convert{$text} = 1; + $offset = hex $1; + $text = $2; + + # if this is either a local function or a weak function + # keep looking for functions that are global that + # we can use safely. + if (!defined($locals{$text}) && !defined($weak{$text})) { + $ref_func = $text; + $read_function = 0; + } else { + # if we already have a function, and this is weak, skip it + if (!defined($ref_func) || !defined($weak{$text})) { + $ref_func = $text; + } } - # is this a call site to mcount? If so, print the offset from the section - } elsif ($text_found && /$mcount_regex/) { - if (!$opened) { - open(FILE, ">$mcount_s") || die "can't create $mcount_s\n"; - $opened = 1; - print FILE "\t.section $mcount_section,\"a\",\@progbits\n"; - } - print FILE "\t$type $text + 0x$1\n"; + } + + # is this a call site to mcount? If so, record it to print later + if ($text_found && /$mcount_regex/) { + $offsets[$#offsets + 1] = hex $1; } } +# dump out anymore offsets that may have been found +update_funcs() if ($text_found); + # If we did not find any mcount callers, we are done (do nothing). if (!$opened) { exit(0); -- cgit v1.2.3 From f2f8458e751f9ae41dfec3c00a46d3e62dc38f60 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 25 Aug 2008 14:52:11 -0400 Subject: ftrace: objcopy version test for local symbols The --globalize-symbols option came out in objcopy version 2.17. If the kernel is being compiled on a system with a lower version of objcopy, then we can not use the globalize / localize trick to link to symbols pointing to local functions. This patch tests the version of objcopy and will only use the trick if the version is greater than or equal to 2.17. Otherwise, if an object has only local functions within a section, it will give a nice warning and recommend the user to upgrade their objcopy. Leaving the symbols unrecorded is not that big of a deal, since the mcount record method changes the actual mcount code to be a simple "ret" without recording registers or anything. Reported-by: Stephen Rothwell Signed-off-by: Steven Rostedt Signed-off-by: Ingo Molnar --- scripts/recordmcount.pl | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'scripts') diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 1891cf9743fc..ee9e12676776 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -186,6 +186,36 @@ if ($filename =~ m,^(.*)(\.\S),) { my $mcount_s = $dirname . "/.tmp_mc_" . $prefix . ".s"; my $mcount_o = $dirname . "/.tmp_mc_" . $prefix . ".o"; +# +# --globalize-symbols came out in 2.17, we must test the version +# of objcopy, and if it is less than 2.17, then we can not +# record local functions. +my $use_locals = 01; +my $local_warn_once = 0; +my $found_version = 0; + +open (IN, "$objcopy --version |") || die "error running $objcopy"; +while () { + if (/objcopy.*\s(\d+)\.(\d+)/) { + my $major = $1; + my $minor = $2; + + $found_version = 1; + if ($major < 2 || + ($major == 2 && $minor < 17)) { + $use_locals = 0; + } + last; + } +} +close (IN); + +if (!$found_version) { + print STDERR "WARNING: could not find objcopy version.\n" . + "\tDisabling local function references.\n"; +} + + # # Step 1: find all the local (static functions) and weak symbols. # 't' is local, 'w/W' is weak (we never use a weak function) @@ -229,6 +259,17 @@ sub update_funcs # is this function static? If so, note this fact. if (defined $locals{$ref_func}) { + + # only use locals if objcopy supports globalize-symbols + if (!$use_locals) { + print STDERR + "$inputfile: WARNING: referencing local function " . + "$ref_func for mcount\n" . + "\tConsider upgrading objcopy to support the globalize-" . + "symbols option.\n" + if (!$local_warn_once++); + return; + } $convert{$ref_func} = 1; } -- cgit v1.2.3 From b3a320417484a6d6b9d28098944df58341353992 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 27 Aug 2008 09:08:30 +0200 Subject: kbuild: ftrace: don't assume that scripts/recordmcount.pl is executable CHK include/linux/version.h CHK include/linux/utsrelease.h CC scripts/mod/empty.o /bin/sh: /usr/src/25/scripts/recordmcount.pl: Permission denied We shouldn't assume that files have their `x' bits set. There are various ways in which file permissions get lost, including use of patch(1). It might not be correct to assume that perl lives in $PATH? Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar --- scripts/Makefile.build | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 232485ec5265..5ed4cbf1e0e1 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -199,8 +199,9 @@ cmd_modversions = \ endif ifdef CONFIG_FTRACE_MCOUNT_RECORD -cmd_record_mcount = $(srctree)/scripts/recordmcount.pl "$(ARCH)" \ - "$(OBJDUMP)" "$(OBJCOPY)" "$(CC)" "$(LD)" "$(NM)" "$(RM)" "$(MV)" "$(@)"; +cmd_record_mcount = perl $(srctree)/scripts/recordmcount.pl \ + "$(ARCH)" "$(OBJDUMP)" "$(OBJCOPY)" "$(CC)" "$(LD)" "$(NM)" "$(RM)" \ + "$(MV)" "$(@)"; endif define rule_cc_o_c -- cgit v1.2.3 From d53475b5aa946752e3306b2ecb5a8c9c51cf8dd0 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 27 Aug 2008 13:02:01 -0400 Subject: ftrace: remove warning of old objcopy and local functions The warning messages about old objcopy and local functions spam the user quite drastically. Remove the warning until we can find a nicer way of tell the user to upgrade their objcopy. Signed-off-by: Steven Rostedt Cc: Stephen Rothwell Signed-off-by: Ingo Molnar --- scripts/recordmcount.pl | 6 ------ 1 file changed, 6 deletions(-) (limited to 'scripts') diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index ee9e12676776..f56d760bd589 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -262,12 +262,6 @@ sub update_funcs # only use locals if objcopy supports globalize-symbols if (!$use_locals) { - print STDERR - "$inputfile: WARNING: referencing local function " . - "$ref_func for mcount\n" . - "\tConsider upgrading objcopy to support the globalize-" . - "symbols option.\n" - if (!$local_warn_once++); return; } $convert{$ref_func} = 1; -- cgit v1.2.3 From aa5d9151f745b6ee6a236a1f109118034277eb92 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Sat, 13 Sep 2008 09:36:06 -0700 Subject: tracing/fastboot: add a script to visualize the kernel boot process / time When optimizing the kernel boot time, it's very valuable to visualize what is going on at which time. In addition, with the fastboot asynchronous initcall level, it's very valuable to see which initcall gets run where and when. This patch adds a script to turn a dmesg into a SVG graph (that can be shown with tools such as InkScape, Gimp or Firefox) and a small change to the initcall code to print the PID of the thread calling the initcall (so that the script can work out the parallelism). Signed-off-by: Arjan van de Ven --- init/main.c | 3 +- scripts/bootgraph.pl | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 scripts/bootgraph.pl (limited to 'scripts') diff --git a/init/main.c b/init/main.c index ded1fae965ab..16abba05c826 100644 --- a/init/main.c +++ b/init/main.c @@ -711,7 +711,8 @@ int do_one_initcall(initcall_t fn) int result; if (initcall_debug) { - printk("calling %pF\n", fn); + printk("calling %pF", fn); + printk(" @ %i\n", task_pid_nr(current)); t0 = ktime_get(); } diff --git a/scripts/bootgraph.pl b/scripts/bootgraph.pl new file mode 100644 index 000000000000..d459b8bdef02 --- /dev/null +++ b/scripts/bootgraph.pl @@ -0,0 +1,138 @@ +#!/usr/bin/perl + +# Copyright 2008, Intel Corporation +# +# This file is part of the Linux kernel +# +# This program file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program in a file named COPYING; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# Authors: +# Arjan van de Ven + + +# +# This script turns a dmesg output into a SVG graphic that shows which +# functions take how much time. You can view SVG graphics with various +# programs, including Inkscape, The Gimp and Firefox. +# +# +# For this script to work, the kernel needs to be compiled with the +# CONFIG_PRINTK_TIME configuration option enabled, and with +# "initcall_debug" passed on the kernel command line. +# +# usage: +# dmesg | perl scripts/bootgraph.pl > output.svg +# + +my @rows; +my %start, %end, %row; +my $done = 0; +my $rowcount = 0; +my $maxtime = 0; +my $count = 0; +while (<>) { + my $line = $_; + if ($line =~ /([0-9\.]+)\] calling ([a-zA-Z\_]+)\+/) { + my $func = $2; + if ($done == 0) { + $start{$func} = $1; + } + $row{$func} = 1; + if ($line =~ /\@ ([0-9]+)/) { + my $pid = $1; + if (!defined($rows[$pid])) { + $rowcount = $rowcount + 1; + $rows[$pid] = $rowcount; + } + $row{$func} = $rows[$pid]; + } + $count = $count + 1; + } + + if ($line =~ /([0-9\.]+)\] initcall ([a-zA-Z\_]+)\+.*returned/) { + if ($done == 0) { + $end{$2} = $1; + $maxtime = $1; + } + } + if ($line =~ /Write protecting the/) { + $done = 1; + } +} + +if ($count == 0) { + print "No data found in the dmesg. Make sure CONFIG_PRINTK_TIME is enabled and\n"; + print "that initcall_debug is passed on the kernel command line.\n\n"; + print "Usage: \n"; + print " dmesg | perl scripts/bootgraph.pl > output.svg\n\n"; + exit; +} + +print " \n"; +print "\n"; + +my @styles; + +$styles[0] = "fill:rgb(0,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[1] = "fill:rgb(0,255,0);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[2] = "fill:rgb(255,0,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[3] = "fill:rgb(255,255,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[4] = "fill:rgb(255,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[5] = "fill:rgb(0,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[6] = "fill:rgb(0,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[7] = "fill:rgb(0,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[8] = "fill:rgb(255,0,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[9] = "fill:rgb(255,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[10] = "fill:rgb(255,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[11] = "fill:rgb(128,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; + +my $mult = 950.0 / $maxtime; +my $threshold = 0.0500 / $maxtime; +my $stylecounter = 0; +while (($key,$value) = each %start) { + my $duration = $end{$key} - $start{$key}; + + if ($duration >= $threshold) { + my $s, $s2, $e, $y; + $s = $value * $mult; + $s2 = $s + 6; + $e = $end{$key} * $mult; + $w = $e - $s; + + $y = $row{$key} * 150; + $y2 = $y + 4; + + $style = $styles[$stylecounter]; + $stylecounter = $stylecounter + 1; + if ($stylecounter > 11) { + $stylecounter = 0; + }; + + print "\n"; + print "$key\n"; + } +} + + +# print the time line on top +my $time = 0.0; +while ($time < $maxtime) { + my $s2 = $time * $mult; + print "$time\n"; + $time = $time + 0.1; +} + +print "\n"; -- cgit v1.2.3 From ddc7a01aad195708fc943d9446411d11e3547784 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 4 Oct 2008 21:35:48 +0200 Subject: tracing/fastboot: fix initcalls disposition in bootgraph.pl When bootgraph.pl parses a file, it gives one row for each initcall's pid. But only few of them will be displayed => the longest. This patch corrects it by giving only a rows for pids which have initcalls that will be displayed. [ mingo@elte.hu: resolved conflicts ] Signed-off-by: Frederic Weisbecker Acked-by: Arjan van de Ven Signed-off-by: Ingo Molnar --- scripts/bootgraph.pl | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'scripts') diff --git a/scripts/bootgraph.pl b/scripts/bootgraph.pl index d459b8bdef02..c34b359c34a3 100644 --- a/scripts/bootgraph.pl +++ b/scripts/bootgraph.pl @@ -37,12 +37,12 @@ # dmesg | perl scripts/bootgraph.pl > output.svg # -my @rows; -my %start, %end, %row; +my %start, %end; my $done = 0; -my $rowcount = 0; my $maxtime = 0; my $count = 0; +my %pids; + while (<>) { my $line = $_; if ($line =~ /([0-9\.]+)\] calling ([a-zA-Z\_]+)\+/) { @@ -50,14 +50,8 @@ while (<>) { if ($done == 0) { $start{$func} = $1; } - $row{$func} = 1; if ($line =~ /\@ ([0-9]+)/) { - my $pid = $1; - if (!defined($rows[$pid])) { - $rowcount = $rowcount + 1; - $rows[$pid] = $rowcount; - } - $row{$func} = $rows[$pid]; + $pids{$func} = $1; } $count = $count + 1; } @@ -102,17 +96,25 @@ $styles[11] = "fill:rgb(128,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb( my $mult = 950.0 / $maxtime; my $threshold = 0.0500 / $maxtime; my $stylecounter = 0; +my %rows; +my $rowscount = 1; while (($key,$value) = each %start) { my $duration = $end{$key} - $start{$key}; if ($duration >= $threshold) { my $s, $s2, $e, $y; - $s = $value * $mult; + $pid = $pids{$key}; + + if (!defined($rows{$pid})) { + $rows{$pid} = $rowscount; + $rowscount = $rowscount + 1; + } + $s = ($value - $firsttime) * $mult; $s2 = $s + 6; $e = $end{$key} * $mult; $w = $e - $s; - $y = $row{$key} * 150; + $y = $rows{$pid} * 150; $y2 = $y + 4; $style = $styles[$stylecounter]; -- cgit v1.2.3 From 80a398a55d3096ff4572b44271d095413749ebb4 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Sun, 14 Sep 2008 15:30:52 -0700 Subject: tracing/fastboot: fix issues and improve output of bootgraph.pl David Sanders reported some issues with bootgraph.pl's display of his sytems bootup; this commit fixes these by scaling the graph not from 0 - end time but from the first initcall to the end time; the minimum display size etc also now need to scale with this, as does the axis display. Signed-off-by: Arjan van de Ven --- scripts/bootgraph.pl | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'scripts') diff --git a/scripts/bootgraph.pl b/scripts/bootgraph.pl index c34b359c34a3..4baf49985589 100644 --- a/scripts/bootgraph.pl +++ b/scripts/bootgraph.pl @@ -40,6 +40,7 @@ my %start, %end; my $done = 0; my $maxtime = 0; +my $firsttime = 100; my $count = 0; my %pids; @@ -49,6 +50,9 @@ while (<>) { my $func = $2; if ($done == 0) { $start{$func} = $1; + if ($1 < $firsttime) { + $firsttime = $1; + } } if ($line =~ /\@ ([0-9]+)/) { $pids{$func} = $1; @@ -65,6 +69,9 @@ while (<>) { if ($line =~ /Write protecting the/) { $done = 1; } + if ($line =~ /Freeing unused kernel memory/) { + $done = 1; + } } if ($count == 0) { @@ -93,8 +100,8 @@ $styles[9] = "fill:rgb(255,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0 $styles[10] = "fill:rgb(255,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; $styles[11] = "fill:rgb(128,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; -my $mult = 950.0 / $maxtime; -my $threshold = 0.0500 / $maxtime; +my $mult = 950.0 / ($maxtime - $firsttime); +my $threshold = ($maxtime - $firsttime) / 60.0; my $stylecounter = 0; my %rows; my $rowscount = 1; @@ -103,15 +110,9 @@ while (($key,$value) = each %start) { if ($duration >= $threshold) { my $s, $s2, $e, $y; - $pid = $pids{$key}; - - if (!defined($rows{$pid})) { - $rows{$pid} = $rowscount; - $rowscount = $rowscount + 1; - } $s = ($value - $firsttime) * $mult; $s2 = $s + 6; - $e = $end{$key} * $mult; + $e = ($end{$key} - $firsttime) * $mult; $w = $e - $s; $y = $rows{$pid} * 150; @@ -130,11 +131,13 @@ while (($key,$value) = each %start) { # print the time line on top -my $time = 0.0; +my $time = $firsttime; +my $step = ($maxtime - $firsttime) / 15; while ($time < $maxtime) { - my $s2 = $time * $mult; - print "$time\n"; - $time = $time + 0.1; + my $s2 = ($time - $firsttime) * $mult; + my $tm = int($time * 100) / 100.0; + print "$tm\n"; + $time = $time + $step; } print "\n"; -- cgit v1.2.3 From 5c542368a3ded88bed98239fb3570dda416203ee Mon Sep 17 00:00:00 2001 From: Arnaud Patard Date: Fri, 19 Sep 2008 20:16:25 -0700 Subject: tracing/fastboot: fix bootgraph.pl initcall name regexp The regexp used to match the start and the end of an initcall are matching only on [a-zA-Z\_]. This rules out initcalls with a number in them. This patch is fixing that. Signed-off-by: Arnaud Patard Signed-off-by: Ingo Molnar --- scripts/bootgraph.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/bootgraph.pl b/scripts/bootgraph.pl index 4baf49985589..479fb4ea8914 100644 --- a/scripts/bootgraph.pl +++ b/scripts/bootgraph.pl @@ -46,7 +46,7 @@ my %pids; while (<>) { my $line = $_; - if ($line =~ /([0-9\.]+)\] calling ([a-zA-Z\_]+)\+/) { + if ($line =~ /([0-9\.]+)\] calling ([a-zA-Z0-9\_]+)\+/) { my $func = $2; if ($done == 0) { $start{$func} = $1; @@ -60,7 +60,7 @@ while (<>) { $count = $count + 1; } - if ($line =~ /([0-9\.]+)\] initcall ([a-zA-Z\_]+)\+.*returned/) { + if ($line =~ /([0-9\.]+)\] initcall ([a-zA-Z0-9\_]+)\+.*returned/) { if ($done == 0) { $end{$2} = $1; $maxtime = $1; @@ -75,8 +75,8 @@ while (<>) { } if ($count == 0) { - print "No data found in the dmesg. Make sure CONFIG_PRINTK_TIME is enabled and\n"; - print "that initcall_debug is passed on the kernel command line.\n\n"; + print "No data found in the dmesg. Make sure that 'printk.time=1' and\n"; + print "'initcall_debug' are passed on the kernel command line.\n\n"; print "Usage: \n"; print " dmesg | perl scripts/bootgraph.pl > output.svg\n\n"; exit; -- cgit v1.2.3 From 07d1890420cce95c577736e4d67f70cbd39845fe Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 4 Oct 2008 21:35:48 +0200 Subject: tracing/fastboot: fix initcalls disposition in bootgraph.pl When bootgraph.pl parses a file, it gives one row for each initcall's pid. But only few of them will be displayed => the longest. This patch corrects it by giving only a rows for pids which have initcalls that will be displayed. Signed-off-by: Frederic Weisbecker Acked-by: Arjan van de Ven Signed-off-by: Ingo Molnar --- scripts/bootgraph.pl | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'scripts') diff --git a/scripts/bootgraph.pl b/scripts/bootgraph.pl index 479fb4ea8914..5e7316e5aa39 100644 --- a/scripts/bootgraph.pl +++ b/scripts/bootgraph.pl @@ -110,6 +110,12 @@ while (($key,$value) = each %start) { if ($duration >= $threshold) { my $s, $s2, $e, $y; + $pid = $pids{$key}; + + if (!defined($rows{$pid})) { + $rows{$pid} = $rowscount; + $rowscount = $rowscount + 1; + } $s = ($value - $firsttime) * $mult; $s2 = $s + 6; $e = ($end{$key} - $firsttime) * $mult; -- cgit v1.2.3