diff --git a/mozilla/tools/reorder/Makefile b/mozilla/tools/reorder/Makefile new file mode 100644 index 00000000000..0ac1e7e0fb4 --- /dev/null +++ b/mozilla/tools/reorder/Makefile @@ -0,0 +1,106 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is ``reordering tools'' +# +# The Initial Developer of the Original Code Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Chris Waterson +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# Redefine this to something that makes sense for you. +MOZ_SRC=/usr/src/seamonkey-clean/mozilla +MKLINKSCRIPT=$(MOZ_SRC)/config/mklinkscript.pl + +ifdef DEBUG +CFLAGS=-g -Wall +CXXFLAGS=-g -Wall +else +CFLAGS=-O2 +CXXFLAGS=-O2 +endif + +ifdef PROFILE +CFLAGS += -pg -g +CXXFLAGS += -pg -g +endif + +TARGETS=\ + libmcount.so \ + libcygprof.so \ + addrs2text \ + garope \ + grope \ + histogram \ + mapaddrs \ + rseed \ + test \ + $(NULL) + +all: $(TARGETS) + +libmcount.so: mcount.c + $(CC) -shared $(CFLAGS) -o $@ $< + +libcygprof.so: cygprof.c + $(CC) -shared $(CFLAGS) -o $@ $< + +addrs2text: addrs2text.o + +garope: garope.cpp elf_symbol_table.o elf_utils.o +grope: grope.cpp elf_symbol_table.o elf_utils.o +histogram: histogram.cpp elf_symbol_table.o elf_utils.o +mapaddrs: mapaddrs.cpp elf_symbol_table.o elf_utils.o +rseed: rseed.c +elf_symbol_table.o: elf_symbol_table.cpp elf_symbol_table.h elf_utils.h interval_map.h +elf_utils.o: elf_utils.cpp elf_utils.h + +# Build these with -pg so we get profiling info +TEST_CFLAGS=-ffunction-sections -finstrument-functions -O2 + +test: test.o mult.o test.ldscript + $(CXX) -Wl,-T,test.ldscript -O2 -o $@ $^ + +test.ldscript: test.order $(MKLINKSCRIPT) + perl $(MKLINKSCRIPT) -o $@ $< + +# This should really be generated by one of the fine tools, above. If +# it hasn't been, create an empty ordering file. +test.order: + touch $@ + +mult.o: mult.c + $(CC) $(TEST_CFLAGS) -c -o $@ $< + +test.o: test.cpp + $(CXX) $(TEST_CFLAGS) -c -o $@ $< + +clean: + rm -f $(TARGETS) test.ldscript *.o *~ core + diff --git a/mozilla/tools/reorder/addrs2text.cpp b/mozilla/tools/reorder/addrs2text.cpp new file mode 100644 index 00000000000..ba0e1002168 --- /dev/null +++ b/mozilla/tools/reorder/addrs2text.cpp @@ -0,0 +1,65 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``addrs2text'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + + Converts a binary stream of address references to a text stream, + hexadecimal, newline separated. + + */ + +#include +#include + +int +main(int argc, char *argv[]) +{ + unsigned int buf[1024]; + ssize_t cb; + + while ((cb = read(STDIN_FILENO, buf, sizeof buf)) > 0) { + if (cb % sizeof buf[0]) + fprintf(stderr, "unaligned read\n"); + + unsigned int *addr = buf; + unsigned int *limit = buf + (cb / 4); + + for (; addr < limit; ++addr) + printf("%x\n", *addr); + } + + return 0; +} diff --git a/mozilla/tools/reorder/cygprof.c b/mozilla/tools/reorder/cygprof.c new file mode 100644 index 00000000000..dec12807b10 --- /dev/null +++ b/mozilla/tools/reorder/cygprof.c @@ -0,0 +1,59 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``libcygprof.so'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + + A stub library to LD_PRELOAD against an image that's been compiled + with -finstrument-functions. It dumps the address of the function + that's being entered upon function entry, and the address that's + being returned to on function exit. Addresses are dumped as binary + data to stdout. + + */ + +#include + +void +__cyg_profile_func_enter(void *this_fn, void *call_site) +{ + write(STDOUT_FILENO, &this_fn, sizeof this_fn); +} + +void +__cyg_profile_func_exit(void *this_fn, void *call_site) +{ + write(STDOUT_FILENO, &call_site, sizeof call_site); +} diff --git a/mozilla/tools/reorder/elf_symbol_table.cpp b/mozilla/tools/reorder/elf_symbol_table.cpp new file mode 100644 index 00000000000..4c0a23bbf3d --- /dev/null +++ b/mozilla/tools/reorder/elf_symbol_table.cpp @@ -0,0 +1,149 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``elf_symbol_table'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "elf_symbol_table.h" +#include "elf_utils.h" + +int +elf_symbol_table::init(const char *name) +{ + // Open the file readonly. + m_fd = open(name, O_RDONLY); + if (m_fd < 0) { + perror(name); + return m_fd; + } + + // Get its size. + struct stat statbuf; + if (fstat(m_fd, &statbuf) < 0) { + perror(name); + return -1; + } + + m_size = statbuf.st_size; + + // Memory map it. + m_mapping = mmap(0, m_size, PROT_READ, MAP_SHARED, m_fd, 0); + if (m_mapping == MAP_FAILED) { + perror(name); + return -1; + } + + // Make sure it's an ELF header. + const Elf32_Ehdr *ehdr = reinterpret_cast(m_mapping); + if (elf_verify_header(ehdr) < 0) + return -1; + + const char *mapping = reinterpret_cast(m_mapping); + + // Find the section headers + const Elf32_Shdr *shdrs + = reinterpret_cast(mapping + ehdr->e_shoff); + + // find the section header string table, .shstrtab + const Elf32_Shdr *shstrtabsh = shdrs + ehdr->e_shstrndx; + const char *shstrtab = mapping + shstrtabsh->sh_offset; + + // parse the sections we care about + int shndx = 0; + const Elf32_Shdr *shlimit = shdrs + ehdr->e_shnum; + for (const Elf32_Shdr *shdr = shdrs; shdr < shlimit; ++shdr, ++shndx) { + basic_string name(shstrtab + shdr->sh_name); + if (name == ".symtab") { + m_symbols = reinterpret_cast(mapping + shdr->sh_offset); + m_nsymbols = shdr->sh_size / sizeof(Elf32_Sym); + } + else if (name == ".strtab") { + m_strtab = mapping + shdr->sh_offset; + } + else if (name == ".text") { + m_text_shndx = shndx; + } + } + + // Parse the symbol table + const Elf32_Sym *limit = m_symbols + m_nsymbols; + for (const Elf32_Sym *sym = m_symbols; sym < limit; ++sym) { + if (is_function(sym)) { +#ifdef DEBUG + hex(cout); + cout << sym->st_value << endl; +#endif + m_rsymtab.put(sym->st_value, sym->st_value + sym->st_size, sym); + } + } + + return 0; +} + +int +elf_symbol_table::finish() +{ + if (m_mapping != MAP_FAILED) { + munmap(m_mapping, m_size); + m_mapping = MAP_FAILED; + } + + if (m_fd >= 0) { + close(m_fd); + m_fd = -1; + } + + return 0; +} + +const Elf32_Sym * +elf_symbol_table::lookup(unsigned int addr) const +{ + rsymtab_t::const_iterator result = m_rsymtab.get(addr); + return result != m_rsymtab.end() ? reinterpret_cast(*result) : 0; +} + +const char * +elf_symbol_table::get_symbol_name(const Elf32_Sym *sym) const +{ + return m_strtab + sym->st_name; +} diff --git a/mozilla/tools/reorder/elf_symbol_table.h b/mozilla/tools/reorder/elf_symbol_table.h new file mode 100644 index 00000000000..bdda0ed2244 --- /dev/null +++ b/mozilla/tools/reorder/elf_symbol_table.h @@ -0,0 +1,114 @@ +/* -*- Mode: C++ -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``elf_symbol_table'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef elf_symbol_table_h__ +#define elf_symbol_table_h__ + +/* + + Utility class for reading ELF symbol tables. + + */ + +#include +#include + +#include "interval_map.h" + +class elf_symbol_table { +protected: + int m_fd; + void *m_mapping; + size_t m_size; + const char *m_strtab; + const Elf32_Sym *m_symbols; + int m_nsymbols; + int m_text_shndx; + + typedef interval_map rsymtab_t; + rsymtab_t m_rsymtab; + + int verify_elf_header(const Elf32_Ehdr *hdr); + int finish(); + +public: + elf_symbol_table() + : m_fd(-1), + m_mapping(MAP_FAILED), + m_size(0), + m_strtab(0), + m_symbols(0), + m_nsymbols(0), + m_text_shndx(0) {} + + ~elf_symbol_table() { finish(); } + + /** + * Read the symbol table information from the specified file. (This will + * memory-map the file.) + * + * Currently, only symbols from the `.text' section are loaded. + */ + int init(const char *file); + + /** + * Determine what symbol the specified image address corresponds + * to. Addresses need not refer to a symbol's start address: + * symbol size information is used to reverse-map the address. + */ + const Elf32_Sym *lookup(unsigned int addr) const; + + /** + * Determine the symbol name for the specified symbol. + */ + const char *get_symbol_name(const Elf32_Sym *sym) const; + + /** + * Return `true' if the specified symbol is a function. + */ + bool is_function(const Elf32_Sym *sym) const { + return (sym->st_size > 0) && + (ELF32_ST_TYPE(sym->st_info) == STT_FUNC) && + (sym->st_shndx == m_text_shndx); } + + typedef const Elf32_Sym *const_iterator; + + const_iterator begin() const { return const_iterator(m_symbols); } + const_iterator end() const { return const_iterator(m_symbols + m_nsymbols); } +}; + +#endif // elf_symbol_table_h__ diff --git a/mozilla/tools/reorder/elf_utils.cpp b/mozilla/tools/reorder/elf_utils.cpp new file mode 100644 index 00000000000..aaa8c65096e --- /dev/null +++ b/mozilla/tools/reorder/elf_utils.cpp @@ -0,0 +1,68 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``elf_symbol_table'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include "elf_utils.h" + +int +elf_verify_header(const Elf32_Ehdr *hdr) +{ + if (hdr->e_ident[EI_MAG0] != ELFMAG0 + || hdr->e_ident[EI_MAG1] != ELFMAG1 + || hdr->e_ident[EI_MAG2] != ELFMAG2 + || hdr->e_ident[EI_MAG3] != ELFMAG3) { + cerr << "not an elf file" << endl; + return -1; + } + + if (hdr->e_ident[EI_CLASS] != ELFCLASS32) { + cerr << "not a 32-bit elf file" << endl; + return -1; + } + + if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) { + cerr << "not a little endian elf file" << endl; + return -1; + } + + if (hdr->e_ident[EI_VERSION] != EV_CURRENT) { + cerr << "incompatible version" << endl; + return -1; + } + + return 0; +} + diff --git a/mozilla/tools/reorder/elf_utils.h b/mozilla/tools/reorder/elf_utils.h new file mode 100644 index 00000000000..2cd05eda845 --- /dev/null +++ b/mozilla/tools/reorder/elf_utils.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++ -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``elf_symbol_table'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef elf_utils_h__ +#define elf_utils_h__ + +/* + + Random utilities for twiddling ELF files. + + */ + +#include "elf.h" + +/** + * Verify that an ELF header is sane. Currently hard-coded to x86. + */ +int +elf_verify_header(const Elf32_Ehdr *hdr); + +#endif diff --git a/mozilla/tools/reorder/func-by-addr.sh b/mozilla/tools/reorder/func-by-addr.sh new file mode 100755 index 00000000000..9855820afe3 --- /dev/null +++ b/mozilla/tools/reorder/func-by-addr.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is ``func-by-addr.sh'' +# +# The Initial Developer of the Original Code Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Chris Waterson +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** +# +# Print the code symbols from an ELF file. +# + +# Find the text segment by searching through the section headers +TEXT=`readelf --section-headers $1 | awk '$2 == ".text" { print $1; }' | sed -e 's/\[//g' -e 's/\]//g'` + +# Now print all of the FUNC-type symbols in the text segment, including +# address, size, and symbol name. +readelf --syms $1 | + awk "\$4 == \"FUNC\" && \$7 == \"${TEXT}\" { printf \"%s %8d %s\n\", \$2, \$3, \$8; }" | + sort -u + diff --git a/mozilla/tools/reorder/garope.cpp b/mozilla/tools/reorder/garope.cpp new file mode 100644 index 00000000000..082de6c53bc --- /dev/null +++ b/mozilla/tools/reorder/garope.cpp @@ -0,0 +1,797 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``garope'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + + A program that attempts to find an optimal function ordering for an + executable using a genetic algorithm whose fitness function is + computed using runtime profile information. + + The fitness function was inspired by Nat Friedman's + work on `grope': + + _GNU Rope - A Subroutine Position Optimizer_ + + + Brendan Eich told me tales about Scott Furman + doing something like this, which sort of made me want to try it. + + As far as I can tell, it would take a lot of computers a lot of time + to actually find something useful on a non-trivial program using + this. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "elf_symbol_table.h" + +#define _GNU_SOURCE +#include + +#define PAGE_SIZE 4096 +#define SYMBOL_ALIGN 4 + +//---------------------------------------------------------------------- + +class call_pair +{ +public: + const Elf32_Sym *m_lo; + const Elf32_Sym *m_hi; + + call_pair(const Elf32_Sym *site1, const Elf32_Sym *site2) + { + if (site1 < site2) { + m_lo = site1; + m_hi = site2; + } + else { + m_hi = site1; + m_lo = site2; + } + } + + friend bool + operator==(const call_pair &lhs, const call_pair &rhs) + { + return (lhs.m_lo == rhs.m_lo) && (lhs.m_hi == rhs.m_hi); + } +}; + +// Straight outta plhash.c! +#define GOLDEN_RATIO 0x9E3779B9U + +template<> +struct hash +{ + size_t operator()(const call_pair &pair) const + { + size_t h = (reinterpret_cast(pair.m_hi) >> 4); + h += (reinterpret_cast(pair.m_lo) >> 4); + h *= GOLDEN_RATIO; + return h; + } +}; + +//---------------------------------------------------------------------- + +struct hash +{ + size_t operator()(const Elf32_Sym *sym) const + { + return (reinterpret_cast(sym) >> 4) * GOLDEN_RATIO; + } +}; + +//---------------------------------------------------------------------- + +typedef hash_map call_graph_t; +call_graph_t call_graph; + +typedef hash_map histogram_t; +histogram_t histogram; +long long total_calls = 0; + +elf_symbol_table symtab; + +bool opt_debug = false; +int opt_generations = 10; +int opt_mutate = 0; +const char *opt_out = "order.out"; +int opt_population_size = 100; +int opt_tick = 0; +bool opt_verbose = false; +int opt_window = 0; + +static struct option long_options[] = { + { "debug", no_argument, 0, 'd' }, + { "exe", required_argument, 0, 'e' }, + { "generations", required_argument, 0, 'g' }, + { "help", no_argument, 0, '?' }, + { "mutate", required_argument, 0, 'm' }, + { "out", required_argument, 0, 'o' }, + { "population", required_argument, 0, 'p' }, + { "seed", required_argument, 0, 's' }, + { "tick", optional_argument, 0, 't' }, + { "verbose", no_argument, 0, 'v' }, + { "window", required_argument, 0, 'w' }, + { 0, 0, 0, 0 } +}; + +//---------------------------------------------------------------------- + +static long long +llrand() +{ + long long result; + result = (long long) rand(); + result *= (long long) (unsigned int) (RAND_MAX + 1); + result += (long long) rand(); + return result; +} + +//---------------------------------------------------------------------- + +class symbol_order { +public: + typedef vector vector_t; + typedef long long score_t; + + static const score_t max_score; + + /** + * A vector of symbols that is this ordering. + */ + vector_t m_ordering; + + /** + * The symbol ordering's score. + */ + score_t m_score; + + symbol_order() : m_score(0) {} + + /** + * ``Shuffle'' a symbol ordering, randomizing it. + */ + void shuffle(); + + /** + * Initialize this symbol ordering by performing a crossover from + * two ``parent'' symbol orderings. + */ + void crossover_from(const symbol_order *father, const symbol_order *mother); + + /** + * Randomly mutate this symbol ordering. + */ + void mutate(); + + /** + * Score a symbol ordering based on paginated locality. + */ + score_t compute_score_page(); + + /** + * Score a symbol ordering based on a sliding window. + */ + score_t compute_score_window(int window_size); + + static score_t compute_score(symbol_order &order); + + /** + * Use the symbol table to dump the ordered symbolic constants. + */ + void dump_symbols() const; + + friend ostream & + operator<<(ostream &out, const symbol_order &order); +}; + +const symbol_order::score_t +symbol_order::max_score = ~((symbol_order::score_t)1 << ((sizeof(symbol_order::score_t) * 8) - 1)); + +symbol_order::score_t +symbol_order::compute_score_page() +{ + m_score = 0; + + unsigned int off = 0; // XXX in reality, probably not page-aligned to start + + vector_t::const_iterator end = m_ordering.end(), + last = end, + sym = m_ordering.begin(); + + while (sym != end) { + vector_t page; + + // If we had a symbol that spilled over from the last page, + // then include it here. + if (last != end) + page.push_back(*last); + + // Pack symbols into the page + do { + page.push_back(*sym); + + int size = (*sym)->st_size; + size += SYMBOL_ALIGN - 1; + size &= ~(SYMBOL_ALIGN - 1); + + off += size; + } while (++sym != end && off < PAGE_SIZE); + + // Remember if there was spill-over. + off %= PAGE_SIZE; + last = (off != 0) ? sym : end; + + // Now score the page as the count of all calls to symbols on + // the page, less calls between the symbols on the page. + vector_t::const_iterator page_end = page.end(); + for (vector_t::const_iterator i = page.begin(); i != page_end; ++i) { + histogram_t::const_iterator func = histogram.find(*i); + if (func == histogram.end()) + continue; + + m_score += func->second; + + vector_t::const_iterator j = i; + for (++j; j != page_end; ++j) { + call_graph_t::const_iterator call = + call_graph.find(call_pair(*i, *j)); + + if (call != call_graph.end()) + m_score -= call->second; + } + } + } + + assert(m_score >= 0); + + // Integer reciprocal so we minimize instead of maximize. + if (m_score == 0) + m_score = 1; + + m_score = (total_calls / m_score) + 1; + + return m_score; +} + +symbol_order::score_t +symbol_order::compute_score_window(int window_size) +{ + m_score = 0; + + vector_t::const_iterator *window = new vector_t::const_iterator[window_size]; + int window_fill = 0; + + vector_t::const_iterator end = m_ordering.end(), + sym = m_ordering.begin(); + + for (; sym != end; ++sym) { + histogram_t::const_iterator func = histogram.find(*sym); + if (func != histogram.end()) { + long long scale = ((long long) 1) << window_size; + + m_score += func->second * scale * 2; + + vector_t::const_iterator *limit = window + window_fill; + vector_t::const_iterator *iter; + for (iter = window ; iter < limit; ++iter) { + call_graph_t::const_iterator call = + call_graph.find(call_pair(*sym, **iter)); + + if (call != call_graph.end()) + m_score -= (call->second * scale); + + scale >>= 1; + } + } + + // Slide the window. + vector_t::const_iterator *begin = window; + vector_t::const_iterator *iter; + for (iter = window + (window_size - 1); iter > begin; --iter) + *iter = *(iter - 1); + + if (window_fill < window_size) + ++window_fill; + + *window = sym; + } + + delete[] window; + + assert(m_score >= 0); + + // Integer reciprocal so we minimize instead of maximize. + if (m_score == 0) + m_score = 1; + + m_score = (total_calls / m_score) + 1; + + return m_score; +} + +symbol_order::score_t +symbol_order::compute_score(symbol_order &order) +{ + if (opt_window) + return order.compute_score_window(opt_window); + + return order.compute_score_page(); +} + +void +symbol_order::shuffle() +{ + vector_t::iterator sym = m_ordering.begin(); + vector_t::iterator end = m_ordering.end(); + for (; sym != end; ++sym) { + int i = rand() % m_ordering.size(); + const Elf32_Sym *temp = *sym; + *sym = m_ordering[i]; + m_ordering[i] = temp; + } +} + +void +symbol_order::crossover_from(const symbol_order *father, const symbol_order *mother) +{ + histogram_t used; + + m_ordering = vector_t(father->m_ordering.size(), 0); + + vector_t::const_iterator parent_sym = father->m_ordering.begin(); + vector_t::iterator sym = m_ordering.begin(); + vector_t::iterator end = m_ordering.end(); + + for (; sym != end; ++sym, ++parent_sym) { + if (rand() % 2) { + *sym = *parent_sym; + used[*parent_sym] = 1; + } + } + + parent_sym = mother->m_ordering.begin(); + sym = m_ordering.begin(); + + for (; sym != end; ++sym) { + if (! *sym) { + while (used[*parent_sym]) + ++parent_sym; + + *sym = *parent_sym++; + } + } +} + +void +symbol_order::mutate() +{ + int i, j; + i = rand() % m_ordering.size(); + j = rand() % m_ordering.size(); + + const Elf32_Sym *temp = m_ordering[i]; + m_ordering[i] = m_ordering[j]; + m_ordering[j] = temp; +} + +void +symbol_order::dump_symbols() const +{ + ofstream out(opt_out); + + vector_t::const_iterator sym = m_ordering.begin(); + vector_t::const_iterator end = m_ordering.end(); + for (; sym != end; ++sym) + out << symtab.get_symbol_name(*sym) << endl; + + out.close(); +} + +ostream & +operator<<(ostream &out, const symbol_order &order) +{ + out << "symbol_order(" << order.m_score << ") "; + + symbol_order::vector_t::const_iterator sym = order.m_ordering.begin(); + symbol_order::vector_t::const_iterator end = order.m_ordering.end(); + for (; sym != end; ++sym) + out.form("%08x ", *sym); + + out << endl; + + return out; +} + +//---------------------------------------------------------------------- + +static void +usage(const char *name) +{ + cerr << "usage: " << name << " [options] [ ...]" << endl; + cerr << " Options:" << endl; + cerr << " --debug, -d" << endl; + cerr << " Print lots of verbose debugging cruft." << endl; + cerr << " --exe=, -e (required)" << endl; + cerr << " Specify the executable image from which to read symbol information." << endl; + cerr << " --generations=, -g " << endl; + cerr << " Specify the number of generations to run the GA (default is 10)." << endl; + cerr << " --help, -?" << endl; + cerr << " Print this message and exit." << endl; + cerr << " --mutate=, -m " << endl; + cerr << " Mutate every th individual, or zero for no mutation (default)." << endl; + cerr << " --out=, -o " << endl; + cerr << " Specify the output file to which to dump the symbol ordering of the" << endl; + cerr << " best individual (default is `order.out')." << endl; + cerr << " --population=, -p " << endl; + cerr << " Set the population size to individuals (default is 100)." << endl; + cerr << " --seed=, -s " << endl; + cerr << " Specify a seed to srand()." << endl; + cerr << " --tick[=], -t []" << endl; + cerr << " When reading address data, print a dot to stderr every th" << endl; + cerr << " address processed from the call trace. If specified with no argument," << endl; + cerr << " a dot will be printed for every million addresses processed." << endl; + cerr << " --verbose, -v" << endl; + cerr << " Issue progress messages to stderr." << endl; + cerr << " --window=, -w " << endl; + cerr << " Use a sliding window instead of pagination to score orderings." << endl; + cerr << endl; + cerr << "This program uses a genetic algorithm to produce an `optimal' ordering for" << endl; + cerr << "an executable based on call patterns." << endl; + cerr << endl; + cerr << "Addresses from a call trace are read as binary data from the files" << endl; + cerr << "specified, or from stdin if no files are specified. These addresses" << endl; + cerr << "are used with the symbolic information from the executable to create" << endl; + cerr << "a call graph. This call graph is used to `score' arbitrary symbol" << endl; + cerr << "orderings, and provides the fitness function for the GA." << endl; + cerr << endl; +} + +/** + * Using the symbol table, map a stream of address references into a + * callgraph and a histogram. + */ +static void +map_addrs(int fd) +{ + const Elf32_Sym *last = 0; + unsigned int buf[128]; + ssize_t cb; + + unsigned int count = 0; + while ((cb = read(fd, buf, sizeof buf)) > 0) { + if (cb % sizeof buf[0]) + fprintf(stderr, "unaligned read\n"); + + unsigned int *addr = buf; + unsigned int *limit = buf + (cb / 4); + + for (; addr < limit; ++addr) { + const Elf32_Sym *sym = symtab.lookup(*addr); + + if (last && sym && last != sym) { + ++total_calls; + ++histogram[sym]; + ++call_graph[call_pair(last, sym)]; + + if (opt_tick && (++count % opt_tick == 0)) { + cerr << "."; + flush(cerr); + } + } + + last = sym; + } + } + + if (opt_tick) + cerr << endl; + + cerr << "Total calls: " << total_calls << endl; + total_calls *= 1024; + + if (opt_window) + total_calls <<= (opt_window + 1); +} + +static symbol_order * +pick_parent(symbol_order *ordering, int max, int index) +{ + while (1) { + index -= ordering->m_score; + if (index < 0) + break; + + ++ordering; + } + + return ordering; +} + +/** + * The main program + */ +int +main(int argc, char *argv[]) +{ + const char *opt_exe = 0; + + int c; + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "?de:g:m:o:p:s:t:vw:", long_options, &option_index); + + if (c < 0) + break; + + switch (c) { + case '?': + usage(argv[0]); + return 0; + + case 'd': + opt_debug = true; + break; + + case 'e': + opt_exe = optarg; + break; + + case 'g': + opt_generations = atoi(optarg); + break; + + case 'm': + opt_mutate = atoi(optarg); + break; + + case 'o': + opt_out = optarg; + break; + + case 'p': + opt_population_size = atoi(optarg); + break; + + case 's': + srand(atoi(optarg)); + break; + + case 't': + opt_tick = optarg ? atoi(optarg) : 1000000; + break; + + case 'v': + opt_verbose = true; + break; + + case 'w': + opt_window = atoi(optarg); + if (opt_window < 0 || opt_window > 8) { + cerr << "invalid window size: " << opt_window << endl; + return 1; + } + + break; + + default: + usage(argv[0]); + return 1; + } + } + + // Make sure an image was specified + if (! opt_exe) { + usage(argv[0]); + return 1; + } + + // Read the sym table. + symtab.init(opt_exe); + + // Process addresses to construct the call graph. + if (optind >= argc) { + map_addrs(STDIN_FILENO); + } + else { + do { + int fd = open(argv[optind], O_RDONLY); + if (fd < 0) { + perror(argv[optind]); + return 1; + } + + map_addrs(fd); + close(fd); + } while (++optind < argc); + } + + if (opt_debug) { + cerr << "Call graph:" << endl; + + call_graph_t::const_iterator limit = call_graph.end(); + call_graph_t::const_iterator i; + for (i = call_graph.begin(); i != limit; ++i) { + const call_pair& pair = i->first; + cerr.form("%08x %08x %10d\n", + pair.m_lo->st_value, + pair.m_hi->st_value, + i->second); + } + } + + // Collect the symbols into a vector + symbol_order::vector_t ordering; + elf_symbol_table::const_iterator end = symtab.end(); + for (elf_symbol_table::const_iterator sym = symtab.begin(); sym != end; ++sym) { + if (symtab.is_function(sym)) + ordering.push_back(sym); + } + + if (opt_verbose) { + symbol_order initial; + initial.m_ordering = ordering; + cerr << "created initial ordering, score=" << symbol_order::compute_score(initial) << endl; + + if (opt_debug) + cerr << initial; + } + + // Create a population. + if (opt_verbose) + cerr << "creating population" << endl; + + symbol_order *population = new symbol_order[opt_population_size]; + + symbol_order::score_t total = 0, min = symbol_order::max_score, max = 0; + + // Score it. + symbol_order *order = population; + symbol_order *limit = population + opt_population_size; + for (; order < limit; ++order) { + order->m_ordering = ordering; + order->shuffle(); + + symbol_order::score_t score = symbol_order::compute_score(*order); + + if (opt_debug) + cerr << *order; + + if (min > score) + min = score; + if (max < score) + max = score; + + total += score; + } + + if (opt_verbose) { + cerr << "Initial population"; + cerr << ": min=" << min; + cerr << ", max=" << max; + cerr << " mean=" << (total / opt_population_size); + cerr << endl; + } + + + // Run the GA. + if (opt_verbose) + cerr << "begininng ga" << endl; + + symbol_order::score_t best = 0; + + for (int generation = 1; generation <= opt_generations; ++generation) { + // Create a new population. + symbol_order *offspring = new symbol_order[opt_population_size]; + + symbol_order *kid = offspring; + symbol_order *offspring_limit = offspring + opt_population_size; + for (; kid < offspring_limit; ++kid) { + // Pick parents. + symbol_order *father, *mother; + father = pick_parent(population, max, llrand() % total); + mother = pick_parent(population, max, llrand() % total); + + // Create a kid. + kid->crossover_from(father, mother); + + // Mutate, possibly. + if (opt_mutate) { + if (rand() % opt_mutate == 0) + kid->mutate(); + } + } + + delete[] population; + population = offspring; + + // Score the new population. + total = 0; + min = symbol_order::max_score; + max = 0; + + symbol_order *fittest = 0; + + limit = offspring_limit; + for (order = population; order < limit; ++order) { + symbol_order::score_t score = symbol_order::compute_score(*order); + + if (opt_debug) + cerr << *order; + + if (min > score) + min = score; + + if (max < score) + max = score; + + if (best < score) { + best = score; + fittest = order; + } + + total += score; + } + + if (opt_verbose) { + cerr << "Generation " << generation; + cerr << ": min=" << min; + cerr << ", max=" << max; + if (fittest) + cerr << "*"; + cerr << " mean=" << (total / opt_population_size); + cerr << endl; + } + + // If we've found a new ``best'' individual, dump it. + if (fittest) + fittest->dump_symbols(); + } + + delete[] population; + return 0; +} diff --git a/mozilla/tools/reorder/grope.cpp b/mozilla/tools/reorder/grope.cpp new file mode 100644 index 00000000000..48b8e905adf --- /dev/null +++ b/mozilla/tools/reorder/grope.cpp @@ -0,0 +1,564 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``grope'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + + A program that computes a function ordering for an executable based + on runtime profile information. + + This program is directly based on work done by Roger Chickering + in + + + + to implement Nat Friedman's `grope', + + _GNU Rope - A Subroutine Position Optimizer_ + + + Specifically, it implements the procedure re-ordering algorithm + described in: + + K. Pettis and R. Hansen. ``Profile-Guided Core Position.'' In + _Proceedings of the Int. Conference on Programming Language Design + and Implementation_, pages 16-27, June 1990. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "elf_symbol_table.h" + +#define _GNU_SOURCE +#include + +elf_symbol_table symtab; + +// Straight outta plhash.c! +#define GOLDEN_RATIO 0x9E3779B9U + +struct hash +{ + size_t operator()(const Elf32_Sym *sym) const { + return (reinterpret_cast(sym) >> 4) * GOLDEN_RATIO; } +}; + +typedef unsigned int call_count_t; + +struct call_graph_arc +{ + const Elf32_Sym *m_from; + const Elf32_Sym *m_to; + call_count_t m_count; + + call_graph_arc(const Elf32_Sym *left, const Elf32_Sym *right, call_count_t count = 0) + : m_count(count) + { + assert(left != right); + + if (left > right) { + m_from = left; + m_to = right; + } + else { + m_from = right; + m_to = left; + } + } + + friend bool + operator==(const call_graph_arc &lhs, const call_graph_arc &rhs) + { + return (lhs.m_from == rhs.m_from) && (lhs.m_to == rhs.m_to); + } + + friend ostream & + operator<<(ostream &out, const call_graph_arc &arc) + { + out << &arc << ": "; + out.form("<(%s %s) %d>", + symtab.get_symbol_name(arc.m_from), + symtab.get_symbol_name(arc.m_to), + arc.m_count); + + return out; + } +}; + +struct hash +{ + size_t + operator()(const call_graph_arc* arc) const + { + size_t result; + result = reinterpret_cast(arc->m_from); + result ^= reinterpret_cast(arc->m_to) >> 16; + result ^= reinterpret_cast(arc->m_to) << 16; + result *= GOLDEN_RATIO; + return result; + } +}; + +struct equal_to +{ + bool + operator()(const call_graph_arc* lhs, const call_graph_arc *rhs) const + { + return *lhs == *rhs; + } +}; + +typedef hash_set arc_container_t; +arc_container_t arcs; + +typedef multimap arc_map_t; +arc_map_t from_map; +arc_map_t to_map; + +struct less_call_graph_arc_count +{ + bool + operator()(const call_graph_arc *lhs, const call_graph_arc *rhs) const + { + if (lhs->m_count == rhs->m_count) { + if (lhs->m_from == rhs->m_from) + return lhs->m_to > rhs->m_to; + + return lhs->m_from > rhs->m_from; + } + return lhs->m_count > rhs->m_count; + } +}; + +typedef set arc_count_index_t; + +bool opt_debug = false; +const char *opt_out = "order.out"; +int opt_tick = 0; +bool opt_verbose = false; +int opt_window = 16; + +static struct option long_options[] = { + { "debug", no_argument, 0, 'd' }, + { "exe", required_argument, 0, 'e' }, + { "help", no_argument, 0, '?' }, + { "out", required_argument, 0, 'o' }, + { "tick", optional_argument, 0, 't' }, + { "verbose", no_argument, 0, 'v' }, + { "window", required_argument, 0, 'w' }, + { 0, 0, 0, 0 } +}; + +//---------------------------------------------------------------------- + +static void +usage(const char *name) +{ + cerr << "usage: " << name << " [options] [ ...]" << endl; + cerr << " Options:" << endl; + cerr << " --debug, -d" << endl; + cerr << " Print lots of verbose debugging cruft." << endl; + cerr << " --exe=, -e (required)" << endl; + cerr << " Specify the executable image from which to read symbol information." << endl; + cerr << " --help, -?" << endl; + cerr << " Print this message and exit." << endl; + cerr << " --out=, -o " << endl; + cerr << " Specify the output file to which to dump the symbol ordering of the" << endl; + cerr << " best individual (default is `order.out')." << endl; + cerr << " --tick[=], -t []" << endl; + cerr << " When reading address data, print a dot to stderr every th" << endl; + cerr << " address processed from the call trace. If specified with no argument," << endl; + cerr << " a dot will be printed for every million addresses processed." << endl; + cerr << " --verbose, -v" << endl; + cerr << " Issue progress messages to stderr." << endl; + cerr << " --window=, -w " << endl; + cerr << " Use a sliding window instead of pagination to score orderings." << endl; +} + +/** + * Using the symbol table, map a stream of address references into a + * callgraph. + */ +static void +map_addrs(int fd) +{ + long long total_calls = 0; + typedef list window_t; + + window_t window; + int window_size = 0; + unsigned int buf[128]; + ssize_t cb; + + while ((cb = read(fd, buf, sizeof buf)) > 0) { + if (cb % sizeof buf[0]) + fprintf(stderr, "unaligned read\n"); + + unsigned int *addr = buf; + unsigned int *limit = buf + (cb / 4); + + for (; addr < limit; ++addr) { + const Elf32_Sym *sym = symtab.lookup(*addr); + if (! sym) + continue; + + ++total_calls; + + window.push_front(sym); + + if (window_size >= opt_window) + window.pop_back(); + else + ++window_size; + + window_t::const_iterator i = window.begin(); + window_t::const_iterator end = window.end(); + for (; i != end; ++i) { + if (sym != *i) { + call_graph_arc *arc; + call_graph_arc key(sym, *i); + arc_container_t::iterator iter = arcs.find(&key); + if (iter == arcs.end()) { + arc = new call_graph_arc(sym, *i); + arcs.insert(arc); + from_map.insert(arc_map_t::value_type(arc->m_from, arc)); + to_map.insert(arc_map_t::value_type(arc->m_to, arc)); + } + else + arc = const_cast(*iter); + + ++(arc->m_count); + } + } + + if (opt_verbose && opt_tick && (total_calls % opt_tick == 0)) { + cerr << "."; + flush(cerr); + } + } + } + + if (opt_verbose) { + if (opt_tick) + cerr << endl; + + cerr << "Total calls: " << total_calls << endl; + } +} + +static void +remove_from(arc_map_t& map, const Elf32_Sym *sym, const call_graph_arc *arc) +{ + pair p + = map.equal_range(sym); + + for (arc_map_t::iterator i = p.first; i != p.second; ++i) { + if (i->second == arc) { + map.erase(i); + break; + } + } +} + +/** + * The main program + */ +int +main(int argc, char *argv[]) +{ + const char *opt_exe = 0; + + int c; + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "?de:o:t:vw:", long_options, &option_index); + + if (c < 0) + break; + + switch (c) { + case '?': + usage(argv[0]); + return 0; + + case 'd': + opt_debug = true; + break; + + case 'e': + opt_exe = optarg; + break; + + case 'o': + opt_out = optarg; + break; + + case 't': + opt_tick = optarg ? atoi(optarg) : 1000000; + break; + + case 'v': + opt_verbose = true; + break; + + case 'w': + opt_window = atoi(optarg); + if (opt_window <= 0) { + cerr << "invalid window size: " << opt_window << endl; + return 1; + } + break; + + default: + usage(argv[0]); + return 1; + } + } + + // Make sure an image was specified + if (! opt_exe) { + usage(argv[0]); + return 1; + } + + // Read the sym table. + symtab.init(opt_exe); + + // Process addresses to construct the weighted call graph. + if (optind >= argc) { + map_addrs(STDIN_FILENO); + } + else { + do { + int fd = open(argv[optind], O_RDONLY); + if (fd < 0) { + perror(argv[optind]); + return 1; + } + + map_addrs(fd); + close(fd); + } while (++optind < argc); + } + + // Emit the ordering. + ofstream out(opt_out); + + // Collect all of the arcs into a sorted container, with arcs + // having the largest weight at the front. + arc_count_index_t sorted_arcs(arcs.begin(), arcs.end()); + + while (sorted_arcs.size()) { + if (opt_debug) { + cerr << "==========================================" << endl << endl; + + cerr << "Sorted Arcs:" << endl; + for (arc_count_index_t::const_iterator iter = sorted_arcs.begin(); + iter != sorted_arcs.end(); + ++iter) { + cerr << **iter << endl; + } + + cerr << endl << "Arc Container:" << endl; + for (arc_container_t::const_iterator iter = arcs.begin(); + iter != arcs.end(); + ++iter) { + cerr << **iter << endl; + } + + cerr << endl << "From Map:" << endl; + for (arc_map_t::const_iterator iter = from_map.begin(); + iter != from_map.end(); + ++iter) { + cerr << symtab.get_symbol_name(iter->first) << ": " << *(iter->second) << endl; + } + + cerr << endl << "To Map:" << endl; + for (arc_map_t::const_iterator iter = to_map.begin(); + iter != to_map.end(); + ++iter) { + cerr << symtab.get_symbol_name(iter->first) << ": " << *(iter->second) << endl; + } + + cerr << endl; + } + + // The first arc in the sorted set will have the largest + // weight. Pull it out, and emit it's sink. + arc_count_index_t::iterator max = sorted_arcs.begin(); + call_graph_arc *arc = const_cast(*max); + + sorted_arcs.erase(max); + + if (opt_debug) + cerr << "pulling " << *arc << endl; + + arcs.erase(arc); + remove_from(from_map, arc->m_from, arc); + remove_from(to_map, arc->m_to, arc); + + out << symtab.get_symbol_name(arc->m_to) << endl; + + // Merge arc->m_to into arc->m_from. First, modify anything + // that used to point to arc->m_to to point to arc->m_from. + typedef list arc_list_t; + arc_list_t map_add_queue; + + pair p; + + // Find all the arcs that point to arc->m_to. + p = to_map.equal_range(arc->m_to); + for (arc_map_t::iterator i = p.first; i != p.second; ++i) { + // Remove the arc that points to arc->m_to (`doomed') from + // all of our indicies. + call_graph_arc *doomed = i->second; + const Elf32_Sym *source = doomed->m_from; + + sorted_arcs.erase(doomed); + arcs.erase(doomed); + remove_from(from_map, source, doomed); + // N.B. that `doomed' will be removed from the `to_map' + // after the loop completes. + + // Create a new (or locate an existing) arc whose source + // was the doomed arc's source, and whose sink is + // arc->m_from (i.e., the node that subsumed arc->m_to). + call_graph_arc *merge; + call_graph_arc key = call_graph_arc(source, arc->m_from); + arc_container_t::iterator iter = arcs.find(&key); + + if (iter == arcs.end()) { + merge = new call_graph_arc(source, arc->m_from); + + arcs.insert(merge); + from_map.insert(arc_map_t::value_type(merge->m_from, merge)); + map_add_queue.push_back(merge); + } + else { + // We found an existing arc: temporarily remove it + // from the sorted index. + merge = const_cast(*iter); + sorted_arcs.erase(merge); + } + + // Include the doomed arc's weight in the merged arc, and + // (re-)insert it into the sorted index. + merge->m_count += doomed->m_count; + sorted_arcs.insert(merge); + + delete doomed; + } + + to_map.erase(p.first, p.second); + + for (arc_list_t::iterator merge = map_add_queue.begin(); + merge != map_add_queue.end(); + map_add_queue.erase(merge++)) { + to_map.insert(arc_map_t::value_type((*merge)->m_to, *merge)); + } + + // Now, roll anything that arc->m_to used to point at into + // what arc->m_from now points at. + + // Collect all of the arcs that originate at arc->m_to. + p = from_map.equal_range(arc->m_to); + for (arc_map_t::iterator i = p.first; i != p.second; ++i) { + // Remove the arc originating from arc->m_to (`doomed') + // from all of our indicies. + call_graph_arc *doomed = i->second; + const Elf32_Sym *sink = doomed->m_to; + + // There shouldn't be any self-referential arcs. + assert(sink != arc->m_to); + + sorted_arcs.erase(doomed); + arcs.erase(doomed); + remove_from(to_map, sink, doomed); + // N.B. that `doomed' will be removed from the `from_map' + // once the loop completes. + + // Create a new (or locate an existing) arc whose source + // is arc->m_from and whose sink was the removed arc's + // sink. + call_graph_arc *merge; + call_graph_arc key = call_graph_arc(arc->m_from, sink); + arc_container_t::iterator iter = arcs.find(&key); + + if (iter == arcs.end()) { + merge = new call_graph_arc(arc->m_from, sink); + + arcs.insert(merge); + + map_add_queue.push_back(merge); + to_map.insert(arc_map_t::value_type(merge->m_to, merge)); + } + else { + // We found an existing arc: temporarily remove it + // from the sorted index. + merge = const_cast(*iter); + sorted_arcs.erase(merge); + } + + // Include the doomed arc's weight in the merged arc, and + // (re-)insert it into the sorted index. + merge->m_count += doomed->m_count; + sorted_arcs.insert(merge); + + delete doomed; + } + + from_map.erase(p.first, p.second); + + for (arc_list_t::iterator merge = map_add_queue.begin(); + merge != map_add_queue.end(); + map_add_queue.erase(merge++)) { + from_map.insert(arc_map_t::value_type((*merge)->m_from, *merge)); + } + } + + out.close(); + return 0; +} diff --git a/mozilla/tools/reorder/histogram.cpp b/mozilla/tools/reorder/histogram.cpp new file mode 100644 index 00000000000..39d044ecd09 --- /dev/null +++ b/mozilla/tools/reorder/histogram.cpp @@ -0,0 +1,174 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``histogram'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + + A program that computes a call histogram from runtime call + information. It reads a list of address references (e.g., as + computed by libcygprof.so), and uses an ELF image to map the + addresses to functions. + + */ + +#include +#include +#include +#include +#include +#include + +#include "elf_symbol_table.h" + +#define _GNU_SOURCE +#include + +const char *opt_exe; +int opt_tick = 0; + +elf_symbol_table table; + +typedef hash_map histogram_t; +histogram_t histogram; + +static struct option long_options[] = { + { "exe", required_argument, 0, 'e' }, + { "tick", optional_argument, 0, 't' }, + { 0, 0, 0, 0 } +}; + +static void +usage(const char *name) +{ + cerr << "usage: " << name << " --exe= [--tick[=count]]" << endl; +} + +static void +map_addrs(int fd) +{ + // Read the binary addresses from stdin. + unsigned int buf[128]; + ssize_t cb; + + unsigned int count = 0; + while ((cb = read(fd, buf, sizeof buf)) > 0) { + if (cb % sizeof buf[0]) + fprintf(stderr, "unaligned read\n"); + + unsigned int *addr = buf; + unsigned int *limit = buf + (cb / 4); + + for (; addr < limit; ++addr) { + const Elf32_Sym *sym = table.lookup(*addr); + if (sym) + ++histogram[reinterpret_cast(sym)]; + + if (opt_tick && (++count % opt_tick == 0)) { + cerr << "."; + flush(cerr); + } + } + } + + if (opt_tick) + cerr << endl; +} + +int +main(int argc, char *argv[]) +{ + int c; + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "e:t", long_options, &option_index); + + if (c < 0) + break; + + switch (c) { + case 'e': + opt_exe = optarg; + break; + + case 't': + opt_tick = optarg ? atoi(optarg) : 1000000; + break; + + default: + usage(argv[0]); + return 1; + } + } + + if (! opt_exe) { + usage(argv[0]); + return 1; + } + + table.init(opt_exe); + + // Process addresses. + if (optind >= argc) { + map_addrs(STDIN_FILENO); + } + else { + do { + int fd = open(argv[optind], O_RDONLY); + if (fd < 0) { + perror(argv[optind]); + return 1; + } + + map_addrs(fd); + close(fd); + } while (++optind < argc); + } + + // Emit the histogram. + histogram_t::const_iterator limit = histogram.end(); + histogram_t::const_iterator i; + for (i = histogram.begin(); i != limit; ++i) { + const Elf32_Sym *sym = reinterpret_cast(i->first); + cout.form("%08x %6d %2d %10d ", + sym->st_value, + sym->st_size, + sym->st_shndx, + i->second); + + cout << table.get_symbol_name(sym) << endl; + } + + return 0; +} diff --git a/mozilla/tools/reorder/interval_map.h b/mozilla/tools/reorder/interval_map.h new file mode 100644 index 00000000000..d99cc0f93f5 --- /dev/null +++ b/mozilla/tools/reorder/interval_map.h @@ -0,0 +1,358 @@ +/* -*- Mode: C++ -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``interval_map'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef interval_map_h__ +#define interval_map_h__ + +/* + + A utility class that maps an interval to an object, allowing clients + to look up the object by a point within the interval. + + */ + +// TODO: +// - removing intervals +// - container iterators + +#include +#include + +template +class interval_map { +protected: + class const_iterator; + friend class const_iterator; + + struct node { + T m_data; + coord m_min; + coord m_max; + node *m_before; // intervals before this one + node *m_within; // intervals within this one + node *m_after; // intervals after this one + int m_bal; + }; + +public: + /** + * A unidirectional const iterator that is used to enumerate the + * intervals that overlap a specific point. + */ + class const_iterator { + protected: + const node *m_node; + const coord m_point; + + friend class interval_map; + const_iterator(const node *n, const coord &point) + : m_node(n), m_point(point) {} + + void advance(); + + public: + const_iterator() : m_node(0), m_point(0) {} + + const_iterator(const const_iterator &iter) + : m_node(iter.m_node), m_point(iter.m_point) {} + + const_iterator & + operator=(const const_iterator &iter) { + m_node = iter.m_node; + m_point = iter.m_point; } + + const T & + operator*() const { return m_node->m_data; } + + const T * + operator->() const { return &m_node->m_data; } + + const_iterator & + operator++() { advance(); return *this; } + + const_iterator + operator++(int) { + const_iterator temp(*this); + advance(); + return temp; } + + bool + operator==(const const_iterator &iter) const { + return m_node == iter.m_node; } + + bool + operator!=(const const_iterator &iter) const { + return !iter.operator==(*this); } + }; + + interval_map() : m_root(0) {} + + ~interval_map() { delete m_root; } + + /** + * Insert aData for the interval [aMin, aMax] + */ + void put(coord min, coord max, const T &data) { + put_into(&m_root, min, max, data); +#ifdef DEBUG + verify(m_root, 0); +#endif + } + + + /** + * Return an iterator that will enumerate the data for all intervals + * intersecting |aPoint|. + */ + const_iterator get(coord point) const; + + /** + * Return an iterator that marks the end-point of iteration. + */ + const_iterator end() const { + return const_iterator(0, 0); } + +protected: + void put_into(node **link, coord min, coord max, const T &data, bool *subsumed = 0); + + void left_rotate(node **link, node *node); + void right_rotate(node **link, node *node); +#ifdef DEBUG + void verify(node *node, int depth); +#endif + node *m_root; +}; + +template +void +interval_map::put_into(node **root, coord min, coord max, const T &data, bool *subsumed) +{ + assert(min < max); + node *interval = *root; + + if (interval) { + bool before = min < interval->m_min; + bool after = max > interval->m_max; + + if (!before || !after) { + // The interval we're adding does not completely subsume + // the |interval|. So we've got one of these situations: + // + // |======| |======| |======| + // |------| |--| |------| + // + // where |==| is the existing interval, and |--| is the + // new interval we're inserting. If there's left or right + // slop, then we ``split'' the new interval in half: + // + // |======| |======| + // |--|---| |---|--| + // + // and insert it both in the ``within'' and ``before'' (or + // ``after'') subtrees. + // + if (before) { + if (max > interval->m_min) { + put_into(&interval->m_within, interval->m_min, max, data); + max = interval->m_min; + } + + bool was_subsumed = true; + put_into(&interval->m_before, min, max, data, &was_subsumed); + + if (! was_subsumed) { + if (interval->m_bal < 0) { + if (interval->m_before->m_bal > 0) + left_rotate(&interval->m_before, interval->m_before); + + right_rotate(root, interval); + } + else + --interval->m_bal; + + if (subsumed) + *subsumed = (interval->m_bal == 0); + } + + return; + } + + if (after) { + if (min < interval->m_max) { + put_into(&interval->m_within, min, interval->m_max, data); + min = interval->m_max; + } + + bool was_subsumed = true; + put_into(&interval->m_after, min, max, data, &was_subsumed); + + if (! was_subsumed) { + if (interval->m_bal > 0) { + if (interval->m_after->m_bal < 0) + right_rotate(&interval->m_after, interval->m_after); + + left_rotate(root, interval); + } + else + ++interval->m_bal; + + if (subsumed) + *subsumed = (interval->m_bal == 0); + } + + return; + } + + put_into(&interval->m_within, min, max, data); + return; + } + + // If we get here, the interval we're adding completely + // subsumes |interval|. We'll go ahead and insert a new + // interval immediately above |interval|, with |interval| as + // the new interval's |m_within|. + } + + if (subsumed) + *subsumed = false; + + node *n = new node(); + n->m_data = data; + n->m_before = n->m_after = 0; + n->m_min = min; + n->m_max = max; + n->m_within = interval; + n->m_bal = 0; + *root = n; +} + +/* + * (*link) (*link) + * | == left rotate ==> | + * (x) (y) + * / \ / \ + * a (y) <== right rotate == (x) c + * / \ / \ + * b c a b + */ +template +void +interval_map::left_rotate(node **link, node *x) +{ + node *y = x->m_after; + x->m_after = y->m_before; + *link = y; + y->m_before = x; + --x->m_bal; + --y->m_bal; +} + +template +void +interval_map::right_rotate(node **link, node *y) +{ + node *x = y->m_before; + y->m_before = x->m_after; + *link = x; + x->m_after = y; + ++y->m_bal; + ++x->m_bal; +} + +template +interval_map::const_iterator +interval_map::get(coord point) const +{ + node *interval = m_root; + + while (interval) { + if (point < interval->m_min) + interval = interval->m_before; + else if (point > interval->m_max) + interval = interval->m_after; + else + break; + } + + return const_iterator(interval, point); +} + + +template +void +interval_map::const_iterator::advance() +{ + assert(m_node); + + m_node = m_node->m_within; + + while (m_node) { + if (m_point < m_node->m_min) + m_node = m_node->m_before; + else if (m_point > m_node->m_max) + m_node = m_node->m_after; + else + break; + } +} + +#ifdef DEBUG +template +void +interval_map::verify(node *node, int depth) +{ + if (node->m_after) + verify(node->m_after, depth + 1); + + for (int i = 0; i < depth; ++i) + cout << " "; + + hex(cout); + cout << node << "("; + dec(cout); + cout << node->m_bal << ")"; + hex(cout); + cout << "[" << node->m_min << "," << node->m_max << "]"; + cout << "@" << node->m_data; + cout << endl; + + if (node->m_before) + verify(node->m_before, depth + 1); +} +#endif // DEBUG + +#endif // interval_map_h__ diff --git a/mozilla/tools/reorder/mapaddrs.cpp b/mozilla/tools/reorder/mapaddrs.cpp new file mode 100644 index 00000000000..8cefcb5557b --- /dev/null +++ b/mozilla/tools/reorder/mapaddrs.cpp @@ -0,0 +1,129 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``mapaddrs'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + + A program that reverse-maps a binary stream of address references to + function names. + + */ + +#include +#include +#include +#include + +#include "elf_symbol_table.h" + +#define _GNU_SOURCE +#include + +static int +map_addrs(int fd, elf_symbol_table &table) +{ + // Read the binary addresses from stdin. + unsigned int buf[128]; + ssize_t cb; + + while ((cb = read(fd, buf, sizeof buf)) > 0) { + if (cb % sizeof buf[0]) + fprintf(stderr, "unaligned read\n"); + + unsigned int *addr = buf; + unsigned int *limit = buf + (cb / 4); + + for (; addr < limit; ++addr) { + const Elf32_Sym *sym = table.lookup(*addr); + if (sym) + cout << table.get_symbol_name(sym) << endl; + } + } + + return 0; +} + +static struct option long_options[] = { + { "exe", required_argument, 0, 'e' }, + { 0, 0, 0, 0 } +}; + +static void +usage() +{ + cerr << "usage: mapaddrs --exe=exe file ..." << endl; +} + +int +main(int argc, char *argv[]) +{ + elf_symbol_table table; + + while (1) { + int option_index = 0; + int c = getopt_long(argc, argv, "e:", long_options, &option_index); + + if (c < 0) + break; + + switch (c) { + case 'e': + table.init(optarg); + break; + + default: + usage(); + return 1; + } + } + + if (optind >= argc) { + map_addrs(STDIN_FILENO, table); + } + else { + do { + int fd = open(argv[optind], O_RDONLY); + if (fd < 0) { + perror(argv[optind]); + return 1; + } + + map_addrs(fd, table); + close(fd); + } while (++optind < argc); + } + + return 0; +} diff --git a/mozilla/tools/reorder/mcount.c b/mozilla/tools/reorder/mcount.c new file mode 100644 index 00000000000..faca56cbac7 --- /dev/null +++ b/mozilla/tools/reorder/mcount.c @@ -0,0 +1,70 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``mcount'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + + A library that can be LD_PRELOAD-ed to dump a function's address on + function entry. + + */ + +#include + +/** + * When compiled with `-pg' (or even just `-p'), gcc inserts a call to + * |mcount| in each function prologue. This implementation of |mcount| + * will grab the return address from the stack, and write it to stdout + * as a binary stream, creating a gross execution trace the + * instrumented program. + * + * For more info on gcc inline assembly, see: + * + * + * + */ +void +mcount() +{ + register unsigned int caller; + unsigned int buf[1]; + + // Grab the return address. + asm("movl 4(%%esp),%0" + : "=r"(caller)); + + buf[0] = caller; + write(STDOUT_FILENO, buf, sizeof buf[0]); +} diff --git a/mozilla/tools/reorder/missing-syms.pl b/mozilla/tools/reorder/missing-syms.pl new file mode 100755 index 00000000000..be8b59d6ac9 --- /dev/null +++ b/mozilla/tools/reorder/missing-syms.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl -w +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is ``syms-by-addr.sh'' +# +# The Initial Developer of the Original Code Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Chris Waterson +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# List the missing symbols (or, do a set difference). +# + +use 5.004; +use strict; +use Getopt::Long; + +$::opt_help = 0; +$::opt_defined = "mozilla-bin.order"; +GetOptions("help", "defined=s"); + +if ($::opt_help) { + die "usage: missing-syms.pl --defined= "; +} + +$::Defined = { }; + +open(DEFINED, "<$::opt_defined") || die "couldn't open defined symbol list $::opt_defined"; +while () { + chomp; + $::Defined{$_} = 1; +} +close(DEFINED); + +while (<>) { + chomp; + print "$_\n" unless ($::Defined{$_}); +} + diff --git a/mozilla/tools/reorder/mult.c b/mozilla/tools/reorder/mult.c new file mode 100644 index 00000000000..adbcc9748bf --- /dev/null +++ b/mozilla/tools/reorder/mult.c @@ -0,0 +1,4 @@ +int mult(int a, int b) +{ + return a * b; +} diff --git a/mozilla/tools/reorder/rseed.c b/mozilla/tools/reorder/rseed.c new file mode 100644 index 00000000000..41a60f62f53 --- /dev/null +++ b/mozilla/tools/reorder/rseed.c @@ -0,0 +1,65 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is ``rseed'' + * + * The Initial Developer of the Original Code is Netscape + * Communications Corp. Portions created by the Initial Developer are + * Copyright (C) 2001 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Waterson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + + A program that reads /dev/random to produce a seed for srand(3). + + */ + +#include +#include + +int +main(int argc, char *argv[]) +{ + int fd, ok, seed = 0; + + fd = open("/dev/random", O_RDONLY); + if (fd < 0) { + perror("/dev/random"); + return 1; + } + + ok = read(fd, &seed, sizeof seed); + if (ok > 0) + printf("%d\n", seed); + else + perror("/dev/random"); + + close(fd); + return 0; +} diff --git a/mozilla/tools/reorder/syms-by-addr.sh b/mozilla/tools/reorder/syms-by-addr.sh new file mode 100755 index 00000000000..72b0d8a7ed4 --- /dev/null +++ b/mozilla/tools/reorder/syms-by-addr.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is ``syms-by-addr.sh'' +# +# The Initial Developer of the Original Code Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Chris Waterson +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# +# Print the code symbols from an ELF file. +# + +# Find the text segment by searching through the section headers +TEXT=`readelf --section-headers $1 | awk '$2 == ".text" { print $1; }' | sed -e 's/\[//g' -e 's/\]//g'` + +# Now print all of the FUNC-type symbols in the text segment, including +# address, size, and symbol name. +readelf --syms $1 | + awk "\$7 == \"${TEXT}\" { printf \"%s %8d %7s %6s %7s %s\n\", \$2, \$3, \$4, \$5, \$6, \$8; }" | + sort -u + diff --git a/mozilla/tools/reorder/test.cpp b/mozilla/tools/reorder/test.cpp new file mode 100644 index 00000000000..8f7b9e8c690 --- /dev/null +++ b/mozilla/tools/reorder/test.cpp @@ -0,0 +1,85 @@ +extern "C" int mult(int l, int r); + +extern "C" { + +inline +int +farfle(int a, int b) +{ + return a * b + a / b; +} + +static +int +ballywhoo(int a, int b) +{ + // So it can't get inlined + if (a > 4) + ballywhoo(a - 1, a + 1); + + return a + b; +} + +static +int +blimpyburger(char a, int b) +{ + if (a == 'f') + return b; + + return 0; +} + +} + +class foo +{ +public: + static int get_flooby() { static int flooby = 12; return flooby; } + + static int divide(int a, int b); + + foo() {} + foo(int a); + virtual ~foo() {} + + int bar(); + + int baz(int a) { return a ? baz(a - 1) : 0; } +}; + +foo::foo(int a) +{ +} + +int +foo::divide(int a, int b) +{ + static int phlegm = 12; + return a / b * ++phlegm; +} + +int +foo::bar() +{ + return 12; +} + +int main(int argc, char* argv[]) +{ + int a = mult(5, 4); + a = ballywhoo(5, 2); + a = farfle(a, 6); + a = blimpyburger('q', 4); + + foo f; + f.get_flooby(); + a = f.bar(); + + a = foo::divide(a, 12) + f.baz(6); + + foo f2(12); + f2.baz(15); + + return a > 99 ? 1 : 0; +}