Bug 632404 - Preload dependent libraries at startup. r=tglek,r=bsmedberg

This commit is contained in:
Mike Hommey 2011-05-22 08:23:20 +02:00
parent 46128c3e45
commit 9148621a36
8 changed files with 247 additions and 6 deletions

View File

@ -209,6 +209,21 @@ int main(int argc, char* argv[])
strcpy(++lastSlash, XPCOM_DLL);
#ifdef XP_WIN
// GetProcessIoCounters().ReadOperationCount seems to have little to
// do with actual read operations. It reports 0 or 1 at this stage
// in the program. Luckily 1 coincides with when prefetch is
// enabled. If Windows prefetch didn't happen we can do our own
// faster dll preloading.
IO_COUNTERS ioCounters;
if (GetProcessIoCounters(GetCurrentProcess(), &ioCounters)
&& !ioCounters.ReadOperationCount)
#endif
{
XPCOMGlueEnablePreload();
}
rv = XPCOMGlueStartup(exePath);
if (NS_FAILED(rv)) {
Output("Couldn't load XPCOM.\n");

View File

@ -48,7 +48,7 @@ XPCOMGlueLoad(const char *xpcomFile, GetFrozenFunctionsFunc *func NS_OUTPARAM);
NS_HIDDEN_(void)
XPCOMGlueUnload();
typedef void (*DependentLibsCallback)(const char *aDependentLib);
typedef void (*DependentLibsCallback)(const char *aDependentLib, PRBool do_preload);
NS_HIDDEN_(void)
XPCOMGlueLoadDependentLibs(const char *xpcomDir, DependentLibsCallback cb);

View File

@ -21,6 +21,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mike Hommey <mh@glandium.org>
*
* 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
@ -39,6 +40,14 @@
#include "nsGlueLinking.h"
#include "nsXPCOMGlue.h"
#ifdef LINUX
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
#include <limits.h>
#endif
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
@ -73,9 +82,69 @@ AppendDependentLib(void *libHandle)
sTop = d;
}
#ifdef LINUX
static const unsigned int bufsize = 4096;
#ifdef HAVE_64BIT_OS
typedef Elf64_Ehdr Elf_Ehdr;
typedef Elf64_Phdr Elf_Phdr;
static const unsigned char ELFCLASS = ELFCLASS64;
typedef Elf64_Off Elf_Off;
#else
typedef Elf32_Ehdr Elf_Ehdr;
typedef Elf32_Phdr Elf_Phdr;
static const unsigned char ELFCLASS = ELFCLASS32;
typedef Elf32_Off Elf_Off;
#endif
static void
ReadDependentCB(const char *aDependentLib)
preload(const char *file)
{
union {
char buf[bufsize];
Elf_Ehdr ehdr;
} elf;
int fd = open(file, O_RDONLY);
if (fd < 0)
return;
// Read ELF header (ehdr) and program header table (phdr).
// We check that the ELF magic is found, that the ELF class matches
// our own, and that the program header table as defined in the ELF
// headers fits in the buffer we read.
if ((read(fd, elf.buf, bufsize) <= 0) ||
(memcmp(elf.buf, ELFMAG, 4)) ||
(elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) ||
(elf.ehdr.e_phoff + elf.ehdr.e_phentsize * elf.ehdr.e_phnum >= bufsize)) {
close(fd);
return;
}
// The program header table contains segment definitions. One such
// segment type is PT_LOAD, which describes how the dynamic loader
// is going to map the file in memory. We use that information to
// find the biggest offset from the library that will be mapped in
// memory.
Elf_Phdr *phdr = (Elf_Phdr *)&elf.buf[elf.ehdr.e_phoff];
Elf_Off end = 0;
for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--)
if ((phdr->p_type == PT_LOAD) &&
(end < phdr->p_offset + phdr->p_filesz))
end = phdr->p_offset + phdr->p_filesz;
// Let the kernel read ahead what the dynamic loader is going to
// map in memory soon after.
if (end > 0) {
readahead(fd, 0, end);
}
close(fd);
}
#endif
static void
ReadDependentCB(const char *aDependentLib, PRBool do_preload)
{
#ifdef LINUX
if (do_preload)
preload(aDependentLib);
#endif
void *libHandle = dlopen(aDependentLib, RTLD_GLOBAL | RTLD_LAZY);
if (!libHandle)
return;

View File

@ -68,7 +68,7 @@ AppendDependentLib(HMODULE libHandle)
}
static void
ReadDependentCB(const char *aDependentLib)
ReadDependentCB(const char *aDependentLib, PRBool do_preload)
{
CHAR pszError[_MAX_PATH];
ULONG ulrc = NO_ERROR;

View File

@ -21,6 +21,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mike Hommey <mh@glandium.org>
*
* 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
@ -45,11 +46,131 @@
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <mach/machine.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <limits.h>
#if defined(__i386__)
static const uint32_t CPU_TYPE = CPU_TYPE_X86;
#elif defined(__x86_64__)
static const uint32_t CPU_TYPE = CPU_TYPE_X86_64;
#elif defined(__ppc__)
static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC;
#elif defined(__ppc64__)
static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC64;
#else
#error Unsupported CPU type
#endif
#ifdef HAVE_64BIT_OS
#undef LC_SEGMENT
#define LC_SEGMENT LC_SEGMENT_64
#undef MH_MAGIC
#define MH_MAGIC MH_MAGIC_64
#define cpu_mach_header mach_header_64
#define segment_command segment_command_64
#else
#define cpu_mach_header mach_header
#endif
class ScopedMMap
{
public:
ScopedMMap(const char *file): buf(NULL) {
fd = open(file, O_RDONLY);
if (fd < 0)
return;
struct stat st;
if (fstat(fd, &st) < 0)
return;
size = st.st_size;
buf = (char *)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
}
~ScopedMMap() {
if (buf)
munmap(buf, size);
if (fd >= 0)
close(fd);
}
operator char *() { return buf; }
int getFd() { return fd; }
private:
int fd;
char *buf;
size_t size;
};
static void
preload(const char *file)
{
ScopedMMap buf(file);
char *base = buf;
if (!base)
return;
// An OSX binary might either be a fat (universal) binary or a
// Mach-O binary. A fat binary actually embeds several Mach-O
// binaries. If we have a fat binary, find the offset where the
// Mach-O binary for our CPU type can be found.
struct fat_header *fh = (struct fat_header *)base;
if (OSSwapBigToHostInt32(fh->magic) == FAT_MAGIC) {
uint32_t nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
struct fat_arch *arch = (struct fat_arch *)&buf[sizeof(struct fat_header)];
for (; nfat_arch; arch++, nfat_arch--) {
if (OSSwapBigToHostInt32(arch->cputype) == CPU_TYPE) {
base += OSSwapBigToHostInt32(arch->offset);
break;
}
}
if (base == buf)
return;
}
// Check Mach-O magic in the Mach header
struct cpu_mach_header *mh = (struct cpu_mach_header *)base;
if (mh->magic != MH_MAGIC)
return;
// The Mach header is followed by a sequence of load commands.
// Each command has a header containing the command type and the
// command size. LD_SEGMENT commands describes how the dynamic
// loader is going to map the file in memory. We use that
// information to find the biggest offset from the library that
// will be mapped in memory.
char *cmd = &base[sizeof(struct cpu_mach_header)];
off_t end = 0;
for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) {
struct segment_command *sh = (struct segment_command *)cmd;
if (sh->cmd != LC_SEGMENT)
continue;
if (end < sh->fileoff + sh->filesize)
end = sh->fileoff + sh->filesize;
cmd += sh->cmdsize;
}
// Let the kernel read ahead what the dynamic loader is going to
// map in memory soon after. The F_RDADVISE fcntl is equivalent
// to Linux' readahead() system call.
if (end > 0) {
struct radvisory ra;
ra.ra_offset = (base - buf);
ra.ra_count = end;
fcntl(buf.getFd(), F_RDADVISE, &ra);
}
}
static const mach_header* sXULLibImage;
static void
ReadDependentCB(const char *aDependentLib)
ReadDependentCB(const char *aDependentLib, PRBool do_preload)
{
if (do_preload)
preload(aDependentLib);
(void) NSAddImage(aDependentLib,
NSADDIMAGE_OPTION_RETURN_ON_ERROR |
NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME);

View File

@ -70,11 +70,34 @@ AppendDependentLib(HINSTANCE libHandle)
}
static void
ReadDependentCB(const char *aDependentLib)
preload(LPCWSTR dll)
{
HANDLE fd = CreateFileW(dll, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
char buf[64 * 1024];
if (fd == INVALID_HANDLE_VALUE)
return;
DWORD dwBytesRead;
// Do dummy reads to trigger kernel-side readhead via FILE_FLAG_SEQUENTIAL_SCAN.
// Abort when underfilling because during testing the buffers are read fully
// A buffer that's not keeping up would imply that readahead isn't working right
while (ReadFile(fd, buf, sizeof(buf), &dwBytesRead, NULL) && dwBytesRead == sizeof(buf))
/* Nothing */;
CloseHandle(fd);
}
static void
ReadDependentCB(const char *aDependentLib, PRBool do_preload)
{
wchar_t wideDependentLib[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, aDependentLib, -1, wideDependentLib, MAX_PATH);
if (do_preload)
preload(wideDependentLib);
HINSTANCE h =
LoadLibraryExW(wideDependentLib, NULL, MOZ_LOADLIBRARY_FLAGS);

View File

@ -55,6 +55,13 @@
#endif
static XPCOMFunctions xpcomFunctions;
static PRBool do_preload = PR_FALSE;
extern "C"
void XPCOMGlueEnablePreload()
{
do_preload = PR_TRUE;
}
extern "C"
nsresult XPCOMGlueStartup(const char* xpcomFile)
@ -128,7 +135,7 @@ XPCOMGlueLoadDependentLibs(const char *xpcomDir, DependentLibsCallback cb)
snprintf(buffer2, sizeof(buffer2),
"%s" XPCOM_FILE_PATH_SEPARATOR "%s",
xpcomDir, buffer);
cb(buffer2);
cb(buffer2, do_preload);
}
fclose(flist);

View File

@ -47,6 +47,12 @@
* The following functions are only available in the standalone glue.
*/
/**
* Enabled preloading of dynamically loaded libraries
*/
extern "C" NS_HIDDEN_(void)
XPCOMGlueEnablePreload();
/**
* Initialize the XPCOM glue by dynamically linking against the XPCOM
* shared library indicated by xpcomFile.