# ---------------------------------------------------------------------- # # CallstackCommon.pm # # A collection of routines that are of use to both # vmkcallstack.pl and vmmCallstack.pl # # ---------------------------------------------------------------------- # package CallstackCommon; require 5.000; use strict; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); use Math::BigInt; # More reliable 64-bit math than 'use bigint', see PR 75451. use File::Copy; use File::Basename; require Exporter; @ISA = qw(Exporter); @EXPORT = qw( SetObjdumpCmd SetFollowDebugLinkCmd CreateViewerScript ExtractVmkernelIDT FindImageFileForVMKModule ParseVMKImageFile CreateVMKSymtab GetImageStaticLoadAddr GetVMKStaticStartAddr GetVmklinuxVersionFromImageFile ); $VERSION = "0.1"; # Default values for commands my $objdumpCmd = undef; my $followDebugLinkCmd = "/sbin/followDebugLink"; # # -------------------------------------------------------------------- # # Set*Cmd -- # # Set command string # # --------------------------------------------------------------------- # sub SetObjdumpCmd($) { $objdumpCmd = shift @_; } sub SetFollowDebugLinkCmd($) { $followDebugLinkCmd = shift @_; } # # -------------------------------------------------------------------- # # GetImageStaticLoadAddr -- # # Find image static load offset # # --------------------------------------------------------------------- # sub GetImageStaticLoadAddr($$) { my $image = shift; my $objdump = shift; my @temp = `$objdump -p $image`; my $checkForRx = 0; my $vaddr = "0x0"; #vaddr is in hexadecimal format foreach my $line (@temp) { if ($line =~ /\s+LOAD off\s+(.*)vaddr(.*)\s+paddr/) { $vaddr = $2; $checkForRx = 1; } elsif (($checkForRx == 1) && ($line =~ /filesz(.*)memsz(.*)flags(.*)x/)) { $checkForRx = 0; last; } else { $checkForRx = 0; $vaddr = "0x0"; } } return $vaddr; } # # ---------------------------------------------------------------------- # # hexcmp -- # # Compares two entries from a vmkernel image list. # The image list is of the form ":::". # We ignore the path and length, and compare the hex addr. # # ---------------------------------------------------------------------- # sub hexcmp($$) { my @firstVec = split(':', shift); my @secondVec = split(':', shift); my $first = Math::BigInt->new($firstVec[0]); my $second = Math::BigInt->new($secondVec[0]); return $first->bacmp($second); } # # ---------------------------------------------------------------------- # # GetVmklinuxVersionFromImageFile -- # # Get vmklinux version from the image file. # # ---------------------------------------------------------------------- # sub GetVmklinuxVersionFromImageFile($) { my $imageFile = shift; #my $vmklinuxVersion = `cat $imageFile | grep vmklinux | grep -i interface`; #if ($vmklinuxVersion =~ /Interface:.*driverAPI_(.*)\s+Built/) { #if ($1 eq "9_2_0_0") { # $vmklinuxVersion = "MN"; #} elsif ($1 eq "9_1") { # $vmklinuxVersion = ""; #} else { # die "can not find vmklinix version"; #} #} else { # die "can not find vmklinix version"; #} return "MN"; } # # ---------------------------------------------------------------------- # # ParseVMKImageFile -- # # Parse the 'image' file created by the vmkstatsdumper program, # and return a list of vmkernel image information. # # The return value is a vector, whose items are of the format # : # The vector will be sorted by the image load address. # # ---------------------------------------------------------------------- # sub ParseVMKImageFile($$$$$$$$) { my $imageFile = shift; my $buildDir = shift; my $imageDirDefault = shift; my $imageLocations = shift; # this is a reference to a hash my $buildType = shift; my $remoteMach = shift; my $verbose = shift; my $tagid = shift; my @imageVec; my @imageList = `cat $imageFile | grep -i $tagid:`; my $image; my %loadAddr; my %length; my %arch; my $vmklinuxVersion = GetVmklinuxVersionFromImageFile($imageFile); # Go through image List and remove duplicates # There may be two entries for an image; one for text section and one for # data. Take the text section one. foreach $image (@imageList) { #tagid:image:loadAddr:length:arch if ($image =~ /^$tagid:(.*):(.*):(.*):(.*)/) { my $addr = Math::BigInt->new($2)->as_hex(); $image = $1; if ($image eq "") { print "WARNING: image name not found, skipping entry in file.\n"; } elsif (!defined($addr) || $addr eq "") { print "WARNING: load address not found. skipping image $image\n"; } elsif ($4 ne "x86_64" && $4 ne "i386" && $4 ne "aarch64") { print "WARNING: invalid arch type $3, skipping image $image.\n"; } elsif (!defined $loadAddr{$image}) { $loadAddr{$image} = $addr;; $length{$image} = Math::BigInt->new($3)->as_hex(); $arch{$image} = $4; } elsif (!Math::BigInt->bcmp($loadAddr{$image}, $addr)) { $loadAddr{$image} = $addr; $length{$image} = Math::BigInt->new($3)->as_hex(); } } } foreach $image (keys %loadAddr) { my $objectFile; $objectFile = FindImageFileForVMKModule($image, $arch{$image}, $vmklinuxVersion, $buildDir, $imageDirDefault, $imageLocations, $buildType, $remoteMach, $verbose); if (!defined($objectFile) || $objectFile eq "" || !-e $objectFile) { print "WARNING: Could not find vmkernel object file for '$image'"; if (defined($objectFile) && $objectFile ne "") { print " (tried \"$objectFile\")"; } print ".\n"; $objectFile = ""; } if ($verbose) { print " $loadAddr{$image} $length{$image} $objectFile\n"; } else { print " $loadAddr{$image} $length{$image} $image\n"; } push(@imageVec, "$loadAddr{$image}:$length{$image}:$image:$objectFile"); } @imageVec = sort hexcmp @imageVec; return @imageVec; } # # ---------------------------------------------------------------------- # # FindImageFile -- # # Given an image name, and the directory of vmkernel modules, # return the full file path of the image file for that module, # if it exists in that directory tree. Checks both regular # files and symbolic links. Checks for files (or links) with # the specified image name, or with the image name plus a ".o" # extension. # # ---------------------------------------------------------------------- # sub FindImageFile($$$$$) { my $image = shift; my $baseDir = shift; my $verbose = shift; my $searchdoto = shift; my $searchsymlink = shift; my $result; if ($verbose) { print "Looking for $image in $baseDir...\n"; } if (!-d $baseDir) { return ""; } # A forward slash at the end of directory path is required otherwise find does not # work in some cases. # Note: Remember to use a relatively minimal subset of # find switches: Visor uses BusyBox not GNU (no -o option # for one thing, which is why we need 4 finds not 2). $result = `find $baseDir/ -type f -name $image | head -n 1`; if ($result eq "" && $searchdoto) { $result = `find $baseDir/ -type f -name $image.o | head -n 1`; } # Check symlinks next if ($result eq "" && $searchsymlink) { $result = `find $baseDir/ -type l -name $image | head -n 1`; if ($result eq "" && $searchdoto) { $result = `find $baseDir/ -type l -name $image.o | head -n 1`; } } chomp $result; return $result; } # # ---------------------------------------------------------------------- # # GetStaticVMKTextStartAddr -- # # Returns the static (i.e. pre-ASLR) load address of vmkernel .text # The result is a Math::BigInt # # This can be done by running # size -Ax vmkernel | grep "\.text" | awk '{print $3}' # however size -Ax doesn't work on visor. # # So instead we run # objdump --syms vmkernel | grep "\.text" | head -n 1 | awk '{print $1}' # # ---------------------------------------------------------------------- # sub GetVMKStaticStartAddr($$$) { my $vmkernel = shift; my $objdump = shift; my $verbose = shift; my $line = `$objdump --syms $vmkernel | grep text | head -n 1`; if ($line =~ /([0-9a-f]+)/){ my $vmkernelStartAddr = Math::BigInt->new("0x".$1)->as_int(); if ($verbose) { print "Non ASLR'd vmkernel start addr is $vmkernelStartAddr\n"; } return $vmkernelStartAddr; } else { die "Unable to determine start address for vmkernel binary."; } } # # ---------------------------------------------------------------------- # # FindImageFileForVMKModule -- # # For the given image, return the full path to the associated object # file. This function will search for the object file in various # directories in the following sequence: # 1. imageLocation for that image if specified. Note this a file # path so if this exists, use this path as-is. # 2. imageLocation for "all" if specified # 3. If not running remotely, default local image directory. # 4. buildDir # # ---------------------------------------------------------------------- sub FindImageFileForVMKModule($$$$$$$$$) { my $image = shift; my $arch = shift; my $vmklinuxVersion = shift; my $buildDir = shift; my $imageDirDefault = shift; my $imageLocations = shift; # this is a reference to a hash my $buildType = shift; my $remoteMach = shift; my $verbose = shift; my $imageFile = ""; # check if the image is in the custom image locations if (defined($imageLocations)) { if ($imageLocations->{$image} and -f $imageLocations->{$image}) { $imageFile = $imageLocations->{$image}; } elsif ($imageLocations->{"all"}) { $imageFile = FindImageFile($image, $imageLocations->{"all"}, $verbose, 0, 0); } } if ($imageFile ne "") { return $imageFile; } # if not remote, check in local default dir first if (!$remoteMach) { $imageFile = FindImageFile($image, $imageDirDefault, $verbose, 1, 1); if ($imageFile ne "") { return $imageFile; } } if (defined $buildDir) { $imageFile = FindImageFileForVisor($image, $arch, $vmklinuxVersion, $buildDir, $remoteMach, $verbose); } return $imageFile; } # # ---------------------------------------------------------------------- # # FindImageFileForClassicEsx -- # # For the given buildType and short object name (e.g., "vmkernel" or # "e1000") return the full path to the associated object file. # # ---------------------------------------------------------------------- sub FindImageFileForClassicEsx($$$$$) { my $image = shift; my $arch = shift; my $vmklinuxVersion = shift; my $buildDir = shift; my $verbose = shift; my $imageFile = ""; # The cosShadow module is just the tcpip module with a different name if ($image eq "cosShadow") { $image = "tcpip3"; } # check if image is in the build tree if ($image eq "vmkernel") { if (-e "$buildDir/vmkernel-vmnix64/vmkernel") { return "$buildDir/vmkernel-vmnix64/vmkernel"; } else { return ""; } } # find in vmkernel modules directory $imageFile = FindImageFile($image, "$buildDir/vmkmod-vmkernel64", $verbose, 0, 1); if ($imageFile ne "") { return $imageFile; } # find in vmkdrivers directory $imageFile = FindImageFile($image, "$buildDir/vmkdrivers-stage64-signed", $verbose, 0, 1); return $imageFile; } # ---------------------------------------------------------------------- # # FindImageFileForVisor -- # # For the given buildType and short object name (e.g., "vmkernel" or # "e1000") return the full path to the associated object file. # # Side effects: # None. # # ---------------------------------------------------------------------- sub FindImageFileForVisor($$$$$) { my $image = shift; my $arch = shift; my $vmklnxVer = shift; my $buildDir = shift; my $remoteMach = shift; my $verbose = shift; my $imageFile = undef; if ($image eq "vmkernel") { if (!$remoteMach && -e "/usr/lib/vmware/vmkmod/vmkernel") { return "/usr/lib/vmware/vmkmod/vmkernel"; } elsif (-e "$buildDir/vmkernel-vmvisor64/vmkernel") { return "$buildDir/vmkernel-vmvisor64/vmkernel"; } else { return ""; } } # check vmkernel modules if (-e "$buildDir/vmvisor/sys-unstripped/usr/lib/vmware/vmkmod/$image") { return "$buildDir/vmvisor/sys-unstripped/usr/lib/vmware/vmkmod/$image"; } elsif (-e "$buildDir/vmvisor/sys-unstripped/usr/lib/vmware/vmkmod/$image.o") { return "$buildDir/vmvisor/sys-unstripped/usr/lib/vmware/vmkmod/$image.o"; } elsif (-e "$buildDir/vmkmod-vmkernel64/$image") { return "$buildDir/vmkmod-vmkernel64/$image"; } elsif (-e "$buildDir/vmkdrivers-stage64/$image") { return "$buildDir/vmkdrivers-stage64/$image"; } elsif (-e "$buildDir/vsan-vmkmod-stage64/$image") { return "$buildDir/vsan-vmkmod-stage64/$image"; } $image = basename($image); # # Vmkernel modules are now distributed all over the place in build tree. # Execute a find only in remote mode. This will save time for local # execution. Furthermore, should exclude libraries from the find as # they do not belong to $buildDir/rpm/. This will also end up with # significant time saving. # if ($remoteMach && !($image =~ /\.so/) && !($image eq "hostd") && !($image =~ "vpxa")) { $imageFile = FindImageFile($image, "$buildDir/rpm", $verbose, 0, 0); if ($imageFile) { return $imageFile; } } if ($image =~ /\.so/) { print("Searching for a shared library image: $image\n"); # check libraries if ($arch =~ /x86_64/) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys/lib64/", $verbose, 0, 1); } else { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys/lib/", $verbose, 0, 1); } # New libraries: look in /usr/lib if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys/usr/lib64/", $verbose, 0, 1); } if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys/usr/lib/", $verbose, 0, 1); } # also look under /vmvisor/sys-boot if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys-boot/lib64/", $verbose, 0, 1); } if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys-boot/lib/", $verbose, 0, 1); } if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys-boot/usr/lib64/", $verbose, 0, 1); } if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys-boot/usr/lib/", $verbose, 0, 1); } # also look under /vmvisor/vim if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/vim/lib64/", $verbose, 0, 1); } if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/vim/lib/", $verbose, 0, 1); } if ($image =~ /libvsanlib/) { $imageFile = "$buildDir/vsan/lib64/libvsanlib.so"; } } elsif ($image eq "vpxa") { $imageFile = FindImageFile($image, "$buildDir/vpxa/", $verbose, 0, 1); if (! -e $imageFile) { $imageFile = ""; } } elsif ($image eq "vmware-vmx") { $imageFile = "$buildDir/vmware-vmx"; } elsif ($image eq "clomd") { $imageFile = "$buildDir/vsan/usr/lib/vmware/vsan/bin/clomd"; } elsif ($image =~ /init.d.clomd/) { $imageFile = "$buildDir/vsan/etc/init.d/clomd"; } else { # check other binaries $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys/sbin/", $verbose, 0, 0); if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys/bin/", $verbose, 0, 0); } if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/vim/bin/", $verbose, 0, 0); } if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys-boot/bin/", $verbose, 0, 0); } # /vmvisor/sys-boot has some binaries if (!$imageFile) { $imageFile = FindImageFile($image, "$buildDir/vmvisor/sys-boot/usr/lib/", $verbose, 0, 1); } } # # Try to get symbols from followDebugLink iff current imageFile has no syms # NOTE: the debug info file might be missing OR not have symbols # print("Image file: $imageFile\n"); if ($imageFile && !imageHasSyms($imageFile) && $followDebugLinkCmd) { if ($verbose) { print("trying to use followDebugLink to find symbol file for $imageFile:\n"); print(" $followDebugLinkCmd $imageFile\n"); } $imageFile = `$followDebugLinkCmd $imageFile`; chomp($imageFile); } if ($verbose) { if ($imageFile) { print("Image: $image Symbol File: $imageFile\n"); } else { print("Image $image has no symbols\n"); } } return $imageFile; } # # ---------------------------------------------------------------------- # # ExtractVmkernelIDT -- # # Tries to determine the address of the vmkernel's IDT address by looking up # the defaultIDTSpace symbol via the mod loader VSI node. # # If successful, returns this pair: # (vmkernel idt start addr (as hex string), # vmkernel idt num bytes (as a Math::BigInt)) # # If unsuccessful, returns ("", 0) [latter a plain integer] # # Useful because a lot of vmkstats samples land in the vmkernel IDT, which # doesn't show up in the symbol table since it's dynamically allocated. # ---------------------------------------------------------------------- # sub ExtractVmkernelIDT($$$$$) { my $buildDir = shift; my $imageDirDefault = shift; my $buildType = shift; my $remoteMach = shift; my $verbose = shift; my $arch = "x86_64"; if ($remoteMach) { print "Not running on an ESX box, can't retrieve defaultIDTSpace symbol\n"; return ("", 0); } my $vmkIdtAddr = ""; open(IDTADDR, "/bin/vsish -e get /system/modloader/symNameToAddr/vmkernel.defaultIDTSpace|") || die "[ExtractVmkernelIDT] Unable to execute vsish command"; $vmkIdtAddr = ; close(IDTADDR); chomp($vmkIdtAddr); # XXX we hard-code the IDT size as 16K. As of this writing, it matches what # the vmkernel allocates, but it would be nicer to be more accurate. my $numBytes = Math::BigInt->new("16") * 1024; if ($verbose) { print "vmkernel IDT $vmkIdtAddr, $numBytes\n"; } return ($vmkIdtAddr, $numBytes); } # # ---------------------------------------------------------------------- # # WriteSymbolTableEntry -- # # Write symbol table entry. # # ---------------------------------------------------------------------- # sub WriteSymbolTableEntry($$$$$$) { my ($outfile, $addr, $length, $func, $longFunctionNames, $image) = @_; print $outfile Math::BigInt->new($addr)->as_hex(), " ", Math::BigInt->new($length)->as_hex(), " ", $func; if ($longFunctionNames) { print $outfile "\@", $image; } print $outfile "\n"; } # ---------------------------------------------------------------------- # # WriteVMKSymtab1Module -- # # Write symbol table entries for a given vmkernel module # into an existing file handle. Symbols will be sorted # by address. # # ---------------------------------------------------------------------- # sub WriteVMKSymtab1Module($$$$$$$) { my $longFunctionNames = shift; my $outfile = shift; my $imageLoadAddr = shift; my $imageLength = shift; my $image = shift; my $imageFile = shift; my $objdump = shift; my $symbolsFound = 1; #find out if the $imageFile has symbols if ($imageFile eq "" or !-e $imageFile) { $symbolsFound = 0; } elsif (`$objdump --syms $imageFile | head -5` =~ /no symbols/) { $symbolsFound = 0; } if (!$symbolsFound) { print "Warning: No symbols found for image $image.", " Adding dummy funcion \@$image. (imageFile = $imageFile).\n"; WriteSymbolTableEntry($outfile, $imageLoadAddr, $imageLength, "", 1, $image); return; } # We use sort, not sort -n, the latter of which doesn't work for hex addrs. # The former only works if hex numbers are all the same length (padded with # zeros on the left if need be). objdump --syms does indeed seem to give us # that. For a more foolproof sort, we could use nm -n, but we had to use # objdump --syms instead of nm, because only the former has --adjust-vma. open(SYMTABLEIN, "$objdump --section=.text --syms $imageFile | grep text | sort | ") || die("unable to run $objdump on $imageFile: $!\n"); # We might have used a dynamic/static load address while loading the image # find the offset my $vaddr = GetImageStaticLoadAddr($imageFile, $objdump); my $offset = Math::BigInt->new($imageLoadAddr)->bsub($vaddr); my $prevsym_addr = Math::BigInt->new(0); my $prevsym_size = Math::BigInt->new(0); my $prevsym_name = ""; my $str; my $firstEntry = 1; while ($str = ) { chomp($str); # The lines from objdump we're interested in will look like one # of these: # # 00600000 l d .text 00000000 # 00666890 g .text 00000000 CommonRet # 0061bb1c g F .text 00000051 IDT_LateInit # 00698868 g F .text 00000019 .hidden Net_EarlyInit # if ($str =~ /^([[:xdigit:]]{8,16})\s+.\s+....\s+(\S+)\s+([[:xdigit:]]{8,16})(\s+\.hidden)?(?:\s+([^\t\r\f\v\n]+))?/) { my ($hexaddr, $section, $hexsize, $name) = ($1, $2, $3, $5); # The ".text" check above should only find text symbols ... if ($section ne '.text') { warn "Non-text line: $str\n"; next; } # Name was not present. This is okay, its just a useless line. # Also check if name begins with .L Discard, as this is a # function-local label next if (not defined($name) or $name eq '' or $name =~ /^\.L/); if (!defined($hexsize) || !defined($hexaddr)) { warn "Malformed line: $str\n"; next; } $hexaddr = "0x$hexaddr"; $hexsize = "0x$hexsize"; my $thissym_addr = Math::BigInt->new($hexaddr) + $offset; my $thissym_size = Math::BigInt->new($hexsize); # Now that we know the current symbol's start address, # we can finally print out the previous symbol, with a non-zero # size (if size had been 0 in the objdump output, as is often # the case). if (!$firstEntry) { if ($prevsym_size == 0) { $prevsym_size = $thissym_addr - $prevsym_addr;; } if ($prevsym_name ne "") { WriteSymbolTableEntry($outfile, $prevsym_addr, $prevsym_size, $prevsym_name, $longFunctionNames, $image); } elsif ($prevsym_addr != 0 && $prevsym_size != 0) { print "dropping symbol with no name, addr= ", $prevsym_addr->as_hex(), " size=", $prevsym_size->as_hex(), "\n"; } } ($prevsym_addr, $prevsym_size, $prevsym_name) = ($thissym_addr, $thissym_size, $name); $firstEntry = 0; } else { warn "Unexpected objdump output: $str\n"; } } # The last symbol: if ($prevsym_size == 0) { # wing it since we have no reliable info $prevsym_size = Math::BigInt->new(100); # } WriteSymbolTableEntry($outfile, $prevsym_addr, $prevsym_size, $prevsym_name, $longFunctionNames, $image); close(SYMTABLEIN); } # # ---------------------------------------------------------------------- # # CreateVMKSymtab -- # # Writes the symbol table of vmkernel to a specified file. # # ---------------------------------------------------------------------- # sub CreateVMKSymtab($$$$$$$$$$) { my $longFunctionNames = shift; my $symbol_table_file = shift; my $imageVecRef = shift; my $objdump = shift; my $buildDir = shift; my $imageDirDefault = shift; my $buildType = shift; my $remoteMach = shift; my $verbose = shift; my $findIDT = shift; my $imageVec = *imageVecRef; my $symbol_table_file1 = "$symbol_table_file" . "1"; # primary temp file my $symbol_table_file2 = "$symbol_table_file" . "2"; # misc stuff like vmkernel IDTs my $symtable1; open($symtable1, ">$symbol_table_file1") || die("unable to open $symbol_table_file1: $!\n"); # Make a useful fake symbol for the vmkernel IDT, which otherwise # won't be found when we iterate through the symbol table # (it's dynamically allocated memory) if ($findIDT) { my ($vmkernelIDTStartAddrStr, $vmkernelIDTNumBytes) = ExtractVmkernelIDT($buildDir, $imageDirDefault, $buildType, $remoteMach, $verbose); if ($vmkernelIDTStartAddrStr eq "") { print "Unknown vmkernel IDT bounds; profile will be slightly less". " descriptive.\nRunning this script as root usually avoids". " that problem.\n"; if ($verbose) { print " Specifically, samples landing in the IDT will be lumped into\n" . " a dummy function \"\" along with all other samples\n" . " that did not land within a symbol table function.\n" . " Usually, running as root corrects this issue (see PR 133306)\n"; } } else { print " $vmkernelIDTStartAddrStr $vmkernelIDTNumBytes vmkernel IDT\n"; system("rm -f $symbol_table_file2"); open(SYMTABLE2, ">$symbol_table_file2") || die "Could not open $symbol_table_file2 for writing\n"; print SYMTABLE2 Math::BigInt->new($vmkernelIDTStartAddrStr)->as_hex(), " ", $vmkernelIDTNumBytes->as_hex(), " ", "\n"; close(SYMTABLE2); } } foreach my $imageEntry (@$imageVecRef) { my ($imageLoadAddr, $imageLength, $image, $objectFile) = split(':', $imageEntry); WriteVMKSymtab1Module($longFunctionNames, $symtable1, $imageLoadAddr, $imageLength, $image, $objectFile, $objdump); } close($symtable1); # Mergesort files $symbol_table_file1 and $symbol_table_file2 if (! -f $symbol_table_file2) { # We use File::move, since Visor doesn't have mv (it has cp & rm). # ESX 3.0's perl didn't have File::move so it's not backwards compatible. File::Copy::move($symbol_table_file1, $symbol_table_file) || die "Could not mv to $symbol_table_file\n"; } else { # -m means merge sort my $cmd = "sort -m $symbol_table_file1 $symbol_table_file2 > $symbol_table_file"; system("$cmd") == 0 || die "Could not run command \"$cmd\"\n"; my $exitval = $? >> 8; if ($exitval != 0 || ! -f $symbol_table_file) { die "Could not mergesort to $symbol_table_file\n"; } # clean up (no particular need to error check) system("rm -f $symbol_table_file1 $symbol_table_file2"); } } # # ---------------------------------------------------------------------- # # CreateViewerScript -- # # Create a shell script to launch the Java UI # # The UI does not take as arguments any information where # to find the callstacks/samples input files: it just looks # for them in the current directory. So, in case the shell # script (that we are presently creating) is launched in *any* # manner other than "./", we have it # perform a cheesy "cd `dirname $0`" before launching java. # # ---- # # Which Java should we use? We know that the toolchain version # works well. But some strongly prefer to use their own local Java # (to avoid toolchain NFS delay, among other reasons). # However, PR 655858 updates #4 and 5 give some cautionary tales on # using your local Java. # The approach we use tries to be a reasonable blend: # 1) If set, we use java from the environment variable CALLSTACKPROF_JAVA. # This mirrors the SHOWDIFFS_JAVA used in make-review. # 2) Else, we first try "java" (assuming it's in the search path), # and only if that returns an error, we try the known good # toolchain version of java. # # ---------------------------------------------------------------------- # sub CreateViewerScript($$) { my $outFileName = shift; # IN: output file name my $tags = shift; # IN: tag(s), e.g. "m g k" for all of # monitor, guest, and vmkernel # # We remove the max heap size option because in most systems heap size should be # one fourth of system memory up to a max of 1 GB. # Minimum max heap size is 64 MB. So, revisit this if users complain # my $lines = <<"DONESENTINEL"; #!/bin/sh # tags: m for monitor, g for guest kernel, k for vmkernel, x for vmx TAGS_DEFAULT="$tags" OS=`uname -s` if [ "\$OS" = "Darwin" ] then TOOLCHAIN_JAVA=/build/toolchain/mac32/java-osx-10.4-release6/usr/bin/java else TOOLCHAIN_JAVA=/build/toolchain/lin32/jdk-1.6.0_13/bin/java fi if [ -z \$JARPATH ]; then if [ -z \$VMTREE ]; then JARPATH=/build/trees/main/bora/support/tools/java/ else JARPATH=\$VMTREE/support/tools/java fi fi JAVA_ARGS="-jar \$JARPATH/vmcallstackview.jar -tag \$TAGS_DEFAULT \$@" try_run() { JAVA=\$1 \$JAVA \$JAVA_ARGS result=\$? if [ \$result -eq 0 ] then # sucessful run; bye exit 0 fi # 127=command not found. 126=not executable. 128+[n]=signal [n]. if [ \$result -eq 127 ] then echo "Could not run \"\$JAVA\" (not found)" elif [ \$result -eq 126 ] then echo "Could not run \"\$JAVA\" (not executable)" elif [ \$result -ge 128 ] then # Ran, and exited with signal. May be innocuous, such as the # user exiting via ctrl-c. Let's exit with the same result code. exit \$result else echo "Could not run \"\$JAVA\", result code \$result" fi } cd `dirname \$0` if [ "\$CALLSTACKPROF_JAVA" != "" ] then try_run \$CALLSTACKPROF_JAVA echo "" echo "The Java binary specified by the CALLSTACKPROF_JAVA environment variable" echo "returned an error code. Please make sure that it points to" echo "a JVM that is at least version 1.6." exit 5 else echo "Environment variable CALLSTACKPROF_JAVA is not set; " echo "trying default java from search path (if any)..." try_run java echo "" echo "java from search path returned an error code; trying" echo "toolchain's java..." try_run \$TOOLCHAIN_JAVA echo "" echo "Sorry, toolchain's java (\$TOOLCHAIN_JAVA) returned an error code as well." echo "Consider installing a local Java that is at least version 1.6, and either" echo "putting it in the search path, or setting CALLSTACKPROF_JAVA to point to it." exit 5 fi DONESENTINEL open CMD, ">$outFileName"; print CMD $lines; close CMD; chmod 0755, $outFileName; } # # -------------------------------------------------------------------- # # imageHasSyms -- # # Check if image file has syms. This is to accomodate followDebugLink # # Results: # 0 if image has "no symbols" in objdump output # 1 if image has some symbols # # --------------------------------------------------------------------- # sub imageHasSyms($) { my $imageFile = shift @_; # # For backward compatibility with vmmCallstack.pl, # return success if objdumpCmd is not set # if (!$objdumpCmd) { return 1; } my $symsOut = `$objdumpCmd -C --syms $imageFile | head -n 20 | grep "no symbols"`; if ($symsOut =~ /no symbols/) { return 0; } return 1; } # All perl .pm files end with this: 1;