diff options
Diffstat (limited to 'tools/profiler/lul')
-rw-r--r-- | tools/profiler/lul/AutoObjectMapper.cpp | 207 | ||||
-rw-r--r-- | tools/profiler/lul/AutoObjectMapper.h | 115 | ||||
-rw-r--r-- | tools/profiler/lul/LulCommon.cpp | 114 | ||||
-rw-r--r-- | tools/profiler/lul/LulDwarf.cpp | 2180 | ||||
-rw-r--r-- | tools/profiler/lul/LulDwarfSummariser.cpp | 359 | ||||
-rw-r--r-- | tools/profiler/lul/LulDwarfSummariser.h | 65 | ||||
-rw-r--r-- | tools/profiler/lul/LulElf.cpp | 915 | ||||
-rw-r--r-- | tools/profiler/lul/LulMain.cpp | 1963 | ||||
-rw-r--r-- | tools/profiler/lul/LulMain.h | 397 | ||||
-rw-r--r-- | tools/profiler/lul/platform-linux-lul.cpp | 88 | ||||
-rw-r--r-- | tools/profiler/lul/platform-linux-lul.h | 24 |
11 files changed, 0 insertions, 6427 deletions
diff --git a/tools/profiler/lul/AutoObjectMapper.cpp b/tools/profiler/lul/AutoObjectMapper.cpp deleted file mode 100644 index 1bc5ce62a..000000000 --- a/tools/profiler/lul/AutoObjectMapper.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include <sys/mman.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -#include "mozilla/Assertions.h" -#include "mozilla/Sprintf.h" - -#include "PlatformMacros.h" -#include "AutoObjectMapper.h" - -#if defined(SPS_OS_android) -# include <dlfcn.h> -# include "mozilla/Types.h" - // FIXME move these out of mozglue/linker/ElfLoader.h into their - // own header, so as to avoid conflicts arising from two definitions - // of Array - extern "C" { - MFBT_API size_t - __dl_get_mappable_length(void *handle); - MFBT_API void * - __dl_mmap(void *handle, void *addr, size_t length, off_t offset); - MFBT_API void - __dl_munmap(void *handle, void *addr, size_t length); - } - // The following are for get_installation_lib_dir() -# include "nsString.h" -# include "nsDirectoryServiceUtils.h" -# include "nsDirectoryServiceDefs.h" -#endif - - -// A helper function for creating failure error messages in -// AutoObjectMapper*::Map. -static void -failedToMessage(void(*aLog)(const char*), - const char* aHowFailed, std::string aFileName) -{ - char buf[300]; - SprintfLiteral(buf, "AutoObjectMapper::Map: Failed to %s \'%s\'", - aHowFailed, aFileName.c_str()); - buf[sizeof(buf)-1] = 0; - aLog(buf); -} - - -AutoObjectMapperPOSIX::AutoObjectMapperPOSIX(void(*aLog)(const char*)) - : mImage(nullptr) - , mSize(0) - , mLog(aLog) - , mIsMapped(false) -{} - -AutoObjectMapperPOSIX::~AutoObjectMapperPOSIX() { - if (!mIsMapped) { - // There's nothing to do. - MOZ_ASSERT(!mImage); - MOZ_ASSERT(mSize == 0); - return; - } - MOZ_ASSERT(mSize > 0); - // The following assertion doesn't necessarily have to be true, - // but we assume (reasonably enough) that no mmap facility would - // be crazy enough to map anything at page zero. - MOZ_ASSERT(mImage); - munmap(mImage, mSize); -} - -bool AutoObjectMapperPOSIX::Map(/*OUT*/void** start, /*OUT*/size_t* length, - std::string fileName) -{ - MOZ_ASSERT(!mIsMapped); - - int fd = open(fileName.c_str(), O_RDONLY); - if (fd == -1) { - failedToMessage(mLog, "open", fileName); - return false; - } - - struct stat st; - int err = fstat(fd, &st); - size_t sz = (err == 0) ? st.st_size : 0; - if (err != 0 || sz == 0) { - failedToMessage(mLog, "fstat", fileName); - close(fd); - return false; - } - - void* image = mmap(nullptr, sz, PROT_READ, MAP_SHARED, fd, 0); - if (image == MAP_FAILED) { - failedToMessage(mLog, "mmap", fileName); - close(fd); - return false; - } - - close(fd); - mIsMapped = true; - mImage = *start = image; - mSize = *length = sz; - return true; -} - - -#if defined(SPS_OS_android) -// A helper function for AutoObjectMapperFaultyLib::Map. Finds out -// where the installation's lib directory is, since we'll have to look -// in there to get hold of libmozglue.so. Returned C string is heap -// allocated and the caller must deallocate it. -static char* -get_installation_lib_dir() -{ - nsCOMPtr<nsIProperties> - directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); - if (!directoryService) { - return nullptr; - } - nsCOMPtr<nsIFile> greDir; - nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), - getter_AddRefs(greDir)); - if (NS_FAILED(rv)) return nullptr; - nsCString path; - rv = greDir->GetNativePath(path); - if (NS_FAILED(rv)) { - return nullptr; - } - return strdup(path.get()); -} - -AutoObjectMapperFaultyLib::AutoObjectMapperFaultyLib(void(*aLog)(const char*)) - : AutoObjectMapperPOSIX(aLog) - , mHdl(nullptr) -{} - -AutoObjectMapperFaultyLib::~AutoObjectMapperFaultyLib() { - if (mHdl) { - // We've got an object mapped by faulty.lib. Unmap it via faulty.lib. - MOZ_ASSERT(mSize > 0); - // Assert on the basis that no valid mapping would start at page zero. - MOZ_ASSERT(mImage); - __dl_munmap(mHdl, mImage, mSize); - dlclose(mHdl); - // Stop assertions in ~AutoObjectMapperPOSIX from failing. - mImage = nullptr; - mSize = 0; - } - // At this point the parent class destructor, ~AutoObjectMapperPOSIX, - // gets called. If that has something mapped in the normal way, it - // will unmap it in the normal way. Unfortunately there's no - // obvious way to enforce the requirement that the object is mapped - // either by faulty.lib or by the parent class, but not by both. -} - -bool AutoObjectMapperFaultyLib::Map(/*OUT*/void** start, /*OUT*/size_t* length, - std::string fileName) -{ - MOZ_ASSERT(!mHdl); - - if (fileName == "libmozglue.so") { - - // Do (2) in the comment above. - char* libdir = get_installation_lib_dir(); - if (libdir) { - fileName = std::string(libdir) + "/lib/" + fileName; - free(libdir); - } - // Hand the problem off to the standard mapper. - return AutoObjectMapperPOSIX::Map(start, length, fileName); - - } else { - - // Do cases (1) and (3) in the comment above. We have to - // grapple with faulty.lib directly. - void* hdl = dlopen(fileName.c_str(), RTLD_GLOBAL | RTLD_LAZY); - if (!hdl) { - failedToMessage(mLog, "get handle for ELF file", fileName); - return false; - } - - size_t sz = __dl_get_mappable_length(hdl); - if (sz == 0) { - dlclose(hdl); - failedToMessage(mLog, "get size for ELF file", fileName); - return false; - } - - void* image = __dl_mmap(hdl, nullptr, sz, 0); - if (image == MAP_FAILED) { - dlclose(hdl); - failedToMessage(mLog, "mmap ELF file", fileName); - return false; - } - - mHdl = hdl; - mImage = *start = image; - mSize = *length = sz; - return true; - } -} - -#endif // defined(SPS_OS_android) diff --git a/tools/profiler/lul/AutoObjectMapper.h b/tools/profiler/lul/AutoObjectMapper.h deleted file mode 100644 index 3f60dc44d..000000000 --- a/tools/profiler/lul/AutoObjectMapper.h +++ /dev/null @@ -1,115 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef AutoObjectMapper_h -#define AutoObjectMapper_h - -#include <string> - -#include "mozilla/Attributes.h" -#include "PlatformMacros.h" - -// A (nearly-) RAII class that maps an object in and then unmaps it on -// destruction. This base class version uses the "normal" POSIX -// functions: open, fstat, close, mmap, munmap. - -class MOZ_STACK_CLASS AutoObjectMapperPOSIX { -public: - // The constructor does not attempt to map the file, because that - // might fail. Instead, once the object has been constructed, - // call Map() to attempt the mapping. There is no corresponding - // Unmap() since the unmapping is done in the destructor. Failure - // messages are sent to |aLog|. - explicit AutoObjectMapperPOSIX(void(*aLog)(const char*)); - - // Unmap the file on destruction of this object. - ~AutoObjectMapperPOSIX(); - - // Map |fileName| into the address space and return the mapping - // extents. If the file is zero sized this will fail. The file is - // mapped read-only and private. Returns true iff the mapping - // succeeded, in which case *start and *length hold its extent. - // Once a call to Map succeeds, all subsequent calls to it will - // fail. - bool Map(/*OUT*/void** start, /*OUT*/size_t* length, std::string fileName); - -protected: - // If we are currently holding a mapped object, these record the - // mapped address range. - void* mImage; - size_t mSize; - - // A logging sink, for complaining about mapping failures. - void (*mLog)(const char*); - -private: - // Are we currently holding a mapped object? This is private to - // the base class. Derived classes need to have their own way to - // track whether they are holding a mapped object. - bool mIsMapped; - - // Disable copying and assignment. - AutoObjectMapperPOSIX(const AutoObjectMapperPOSIX&); - AutoObjectMapperPOSIX& operator=(const AutoObjectMapperPOSIX&); - // Disable heap allocation of this class. - void* operator new(size_t); - void* operator new[](size_t); - void operator delete(void*); - void operator delete[](void*); -}; - - -#if defined(SPS_OS_android) -// This is a variant of AutoObjectMapperPOSIX suitable for use in -// conjunction with faulty.lib on Android. How it behaves depends on -// the name of the file to be mapped. There are three possible cases: -// -// (1) /foo/bar/xyzzy/blah.apk!/libwurble.so -// We hand it as-is to faulty.lib and let it fish the relevant -// bits out of the APK. -// -// (2) libmozglue.so -// This is part of the Fennec installation, but is not in the -// APK. Instead we have to figure out the installation path -// and look for it there. Because of faulty.lib limitations, -// we have to use regular open/mmap instead of faulty.lib. -// -// (3) libanythingelse.so -// faulty.lib assumes this is a system library, and prepends -// "/system/lib/" to the path. So as in (1), we can give it -// as-is to faulty.lib. -// -// Hence (1) and (3) require special-casing here. Case (2) simply -// hands the problem to the parent class. - -class MOZ_STACK_CLASS AutoObjectMapperFaultyLib : public AutoObjectMapperPOSIX { -public: - AutoObjectMapperFaultyLib(void(*aLog)(const char*)); - - ~AutoObjectMapperFaultyLib(); - - bool Map(/*OUT*/void** start, /*OUT*/size_t* length, std::string fileName); - -private: - // faulty.lib requires us to maintain an abstract handle that can be - // used later to unmap the area. If this is non-NULL, it is assumed - // that unmapping is to be done by faulty.lib. Otherwise it goes - // via the normal mechanism. - void* mHdl; - - // Disable copying and assignment. - AutoObjectMapperFaultyLib(const AutoObjectMapperFaultyLib&); - AutoObjectMapperFaultyLib& operator=(const AutoObjectMapperFaultyLib&); - // Disable heap allocation of this class. - void* operator new(size_t); - void* operator new[](size_t); - void operator delete(void*); - void operator delete[](void*); -}; - -#endif // defined(SPS_OS_android) - -#endif // AutoObjectMapper_h diff --git a/tools/profiler/lul/LulCommon.cpp b/tools/profiler/lul/LulCommon.cpp deleted file mode 100644 index 7321251c8..000000000 --- a/tools/profiler/lul/LulCommon.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ - -// Copyright (c) 2011, 2013 Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> - - -// This file is derived from the following files in -// toolkit/crashreporter/google-breakpad: -// src/common/module.cc -// src/common/unique_string.cc - -// There's no internal-only interface for LulCommon. Hence include -// the external interface directly. -#include "LulCommonExt.h" - -#include <stdlib.h> -#include <string.h> - -#include <string> -#include <map> - - -namespace lul { - -using std::string; - -//////////////////////////////////////////////////////////////// -// Module -// -Module::Module(const string &name, const string &os, - const string &architecture, const string &id) : - name_(name), - os_(os), - architecture_(architecture), - id_(id) { } - -Module::~Module() { -} - - -//////////////////////////////////////////////////////////////// -// UniqueString -// -class UniqueString { - public: - explicit UniqueString(string str) { str_ = strdup(str.c_str()); } - ~UniqueString() { free(reinterpret_cast<void*>(const_cast<char*>(str_))); } - const char* str_; -}; - -const char* FromUniqueString(const UniqueString* ustr) -{ - return ustr->str_; -} - -bool IsEmptyUniqueString(const UniqueString* ustr) -{ - return (ustr->str_)[0] == '\0'; -} - - -//////////////////////////////////////////////////////////////// -// UniqueStringUniverse -// -UniqueStringUniverse::~UniqueStringUniverse() -{ - for (std::map<string, UniqueString*>::iterator it = map_.begin(); - it != map_.end(); it++) { - delete it->second; - } -} - -const UniqueString* UniqueStringUniverse::ToUniqueString(string str) -{ - std::map<string, UniqueString*>::iterator it = map_.find(str); - if (it == map_.end()) { - UniqueString* ustr = new UniqueString(str); - map_[str] = ustr; - return ustr; - } else { - return it->second; - } -} - -} // namespace lul diff --git a/tools/profiler/lul/LulDwarf.cpp b/tools/profiler/lul/LulDwarf.cpp deleted file mode 100644 index 1bdbdabb6..000000000 --- a/tools/profiler/lul/LulDwarf.cpp +++ /dev/null @@ -1,2180 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ - -// Copyright (c) 2010 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> -// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> - -// Implementation of dwarf2reader::LineInfo, dwarf2reader::CompilationUnit, -// and dwarf2reader::CallFrameInfo. See dwarf2reader.h for details. - -// This file is derived from the following files in -// toolkit/crashreporter/google-breakpad: -// src/common/dwarf/bytereader.cc -// src/common/dwarf/dwarf2reader.cc -// src/common/dwarf_cfi_to_module.cc - -#include <stdint.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -#include <map> -#include <stack> -#include <string> - -#include "mozilla/Assertions.h" -#include "mozilla/Sprintf.h" - -#include "LulCommonExt.h" -#include "LulDwarfInt.h" - - -// Set this to 1 for verbose logging -#define DEBUG_DWARF 0 - - -namespace lul { - -using std::string; - -ByteReader::ByteReader(enum Endianness endian) - :offset_reader_(NULL), address_reader_(NULL), endian_(endian), - address_size_(0), offset_size_(0), - have_section_base_(), have_text_base_(), have_data_base_(), - have_function_base_() { } - -ByteReader::~ByteReader() { } - -void ByteReader::SetOffsetSize(uint8 size) { - offset_size_ = size; - MOZ_ASSERT(size == 4 || size == 8); - if (size == 4) { - this->offset_reader_ = &ByteReader::ReadFourBytes; - } else { - this->offset_reader_ = &ByteReader::ReadEightBytes; - } -} - -void ByteReader::SetAddressSize(uint8 size) { - address_size_ = size; - MOZ_ASSERT(size == 4 || size == 8); - if (size == 4) { - this->address_reader_ = &ByteReader::ReadFourBytes; - } else { - this->address_reader_ = &ByteReader::ReadEightBytes; - } -} - -uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) { - const uint64 initial_length = ReadFourBytes(start); - start += 4; - - // In DWARF2/3, if the initial length is all 1 bits, then the offset - // size is 8 and we need to read the next 8 bytes for the real length. - if (initial_length == 0xffffffff) { - SetOffsetSize(8); - *len = 12; - return ReadOffset(start); - } else { - SetOffsetSize(4); - *len = 4; - } - return initial_length; -} - -bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const { - if (encoding == DW_EH_PE_omit) return true; - if (encoding == DW_EH_PE_aligned) return true; - if ((encoding & 0x7) > DW_EH_PE_udata8) - return false; - if ((encoding & 0x70) > DW_EH_PE_funcrel) - return false; - return true; -} - -bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const { - switch (encoding & 0x70) { - case DW_EH_PE_absptr: return true; - case DW_EH_PE_pcrel: return have_section_base_; - case DW_EH_PE_textrel: return have_text_base_; - case DW_EH_PE_datarel: return have_data_base_; - case DW_EH_PE_funcrel: return have_function_base_; - default: return false; - } -} - -uint64 ByteReader::ReadEncodedPointer(const char *buffer, - DwarfPointerEncoding encoding, - size_t *len) const { - // UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't - // see it here. - MOZ_ASSERT(encoding != DW_EH_PE_omit); - - // The Linux Standards Base 4.0 does not make this clear, but the - // GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c) - // agree that aligned pointers are always absolute, machine-sized, - // machine-signed pointers. - if (encoding == DW_EH_PE_aligned) { - MOZ_ASSERT(have_section_base_); - - // We don't need to align BUFFER in *our* address space. Rather, we - // need to find the next position in our buffer that would be aligned - // when the .eh_frame section the buffer contains is loaded into the - // program's memory. So align assuming that buffer_base_ gets loaded at - // address section_base_, where section_base_ itself may or may not be - // aligned. - - // First, find the offset to START from the closest prior aligned - // address. - uint64 skew = section_base_ & (AddressSize() - 1); - // Now find the offset from that aligned address to buffer. - uint64 offset = skew + (buffer - buffer_base_); - // Round up to the next boundary. - uint64 aligned = (offset + AddressSize() - 1) & -AddressSize(); - // Convert back to a pointer. - const char *aligned_buffer = buffer_base_ + (aligned - skew); - // Finally, store the length and actually fetch the pointer. - *len = aligned_buffer - buffer + AddressSize(); - return ReadAddress(aligned_buffer); - } - - // Extract the value first, ignoring whether it's a pointer or an - // offset relative to some base. - uint64 offset; - switch (encoding & 0x0f) { - case DW_EH_PE_absptr: - // DW_EH_PE_absptr is weird, as it is used as a meaningful value for - // both the high and low nybble of encoding bytes. When it appears in - // the high nybble, it means that the pointer is absolute, not an - // offset from some base address. When it appears in the low nybble, - // as here, it means that the pointer is stored as a normal - // machine-sized and machine-signed address. A low nybble of - // DW_EH_PE_absptr does not imply that the pointer is absolute; it is - // correct for us to treat the value as an offset from a base address - // if the upper nybble is not DW_EH_PE_absptr. - offset = ReadAddress(buffer); - *len = AddressSize(); - break; - - case DW_EH_PE_uleb128: - offset = ReadUnsignedLEB128(buffer, len); - break; - - case DW_EH_PE_udata2: - offset = ReadTwoBytes(buffer); - *len = 2; - break; - - case DW_EH_PE_udata4: - offset = ReadFourBytes(buffer); - *len = 4; - break; - - case DW_EH_PE_udata8: - offset = ReadEightBytes(buffer); - *len = 8; - break; - - case DW_EH_PE_sleb128: - offset = ReadSignedLEB128(buffer, len); - break; - - case DW_EH_PE_sdata2: - offset = ReadTwoBytes(buffer); - // Sign-extend from 16 bits. - offset = (offset ^ 0x8000) - 0x8000; - *len = 2; - break; - - case DW_EH_PE_sdata4: - offset = ReadFourBytes(buffer); - // Sign-extend from 32 bits. - offset = (offset ^ 0x80000000ULL) - 0x80000000ULL; - *len = 4; - break; - - case DW_EH_PE_sdata8: - // No need to sign-extend; this is the full width of our type. - offset = ReadEightBytes(buffer); - *len = 8; - break; - - default: - abort(); - } - - // Find the appropriate base address. - uint64 base; - switch (encoding & 0x70) { - case DW_EH_PE_absptr: - base = 0; - break; - - case DW_EH_PE_pcrel: - MOZ_ASSERT(have_section_base_); - base = section_base_ + (buffer - buffer_base_); - break; - - case DW_EH_PE_textrel: - MOZ_ASSERT(have_text_base_); - base = text_base_; - break; - - case DW_EH_PE_datarel: - MOZ_ASSERT(have_data_base_); - base = data_base_; - break; - - case DW_EH_PE_funcrel: - MOZ_ASSERT(have_function_base_); - base = function_base_; - break; - - default: - abort(); - } - - uint64 pointer = base + offset; - - // Remove inappropriate upper bits. - if (AddressSize() == 4) - pointer = pointer & 0xffffffff; - else - MOZ_ASSERT(AddressSize() == sizeof(uint64)); - - return pointer; -} - - -// A DWARF rule for recovering the address or value of a register, or -// computing the canonical frame address. There is one subclass of this for -// each '*Rule' member function in CallFrameInfo::Handler. -// -// It's annoying that we have to handle Rules using pointers (because -// the concrete instances can have an arbitrary size). They're small, -// so it would be much nicer if we could just handle them by value -// instead of fretting about ownership and destruction. -// -// It seems like all these could simply be instances of std::tr1::bind, -// except that we need instances to be EqualityComparable, too. -// -// This could logically be nested within State, but then the qualified names -// get horrendous. -class CallFrameInfo::Rule { - public: - virtual ~Rule() { } - - // Tell HANDLER that, at ADDRESS in the program, REGISTER can be - // recovered using this rule. If REGISTER is kCFARegister, then this rule - // describes how to compute the canonical frame address. Return what the - // HANDLER member function returned. - virtual bool Handle(Handler *handler, uint64 address, int register) const = 0; - - // Equality on rules. We use these to decide which rules we need - // to report after a DW_CFA_restore_state instruction. - virtual bool operator==(const Rule &rhs) const = 0; - - bool operator!=(const Rule &rhs) const { return ! (*this == rhs); } - - // Return a pointer to a copy of this rule. - virtual Rule *Copy() const = 0; - - // If this is a base+offset rule, change its base register to REG. - // Otherwise, do nothing. (Ugly, but required for DW_CFA_def_cfa_register.) - virtual void SetBaseRegister(unsigned reg) { } - - // If this is a base+offset rule, change its offset to OFFSET. Otherwise, - // do nothing. (Ugly, but required for DW_CFA_def_cfa_offset.) - virtual void SetOffset(long long offset) { } - - // A RTTI workaround, to make it possible to implement equality - // comparisons on classes derived from this one. - enum CFIRTag { - CFIR_UNDEFINED_RULE, - CFIR_SAME_VALUE_RULE, - CFIR_OFFSET_RULE, - CFIR_VAL_OFFSET_RULE, - CFIR_REGISTER_RULE, - CFIR_EXPRESSION_RULE, - CFIR_VAL_EXPRESSION_RULE - }; - - // Produce the tag that identifies the child class of this object. - virtual CFIRTag getTag() const = 0; -}; - -// Rule: the value the register had in the caller cannot be recovered. -class CallFrameInfo::UndefinedRule: public CallFrameInfo::Rule { - public: - UndefinedRule() { } - ~UndefinedRule() { } - CFIRTag getTag() const { return CFIR_UNDEFINED_RULE; } - bool Handle(Handler *handler, uint64 address, int reg) const { - return handler->UndefinedRule(address, reg); - } - bool operator==(const Rule &rhs) const { - if (rhs.getTag() != CFIR_UNDEFINED_RULE) return false; - return true; - } - Rule *Copy() const { return new UndefinedRule(*this); } -}; - -// Rule: the register's value is the same as that it had in the caller. -class CallFrameInfo::SameValueRule: public CallFrameInfo::Rule { - public: - SameValueRule() { } - ~SameValueRule() { } - CFIRTag getTag() const { return CFIR_SAME_VALUE_RULE; } - bool Handle(Handler *handler, uint64 address, int reg) const { - return handler->SameValueRule(address, reg); - } - bool operator==(const Rule &rhs) const { - if (rhs.getTag() != CFIR_SAME_VALUE_RULE) return false; - return true; - } - Rule *Copy() const { return new SameValueRule(*this); } -}; - -// Rule: the register is saved at OFFSET from BASE_REGISTER. BASE_REGISTER -// may be CallFrameInfo::Handler::kCFARegister. -class CallFrameInfo::OffsetRule: public CallFrameInfo::Rule { - public: - OffsetRule(int base_register, long offset) - : base_register_(base_register), offset_(offset) { } - ~OffsetRule() { } - CFIRTag getTag() const { return CFIR_OFFSET_RULE; } - bool Handle(Handler *handler, uint64 address, int reg) const { - return handler->OffsetRule(address, reg, base_register_, offset_); - } - bool operator==(const Rule &rhs) const { - if (rhs.getTag() != CFIR_OFFSET_RULE) return false; - const OffsetRule *our_rhs = static_cast<const OffsetRule *>(&rhs); - return (base_register_ == our_rhs->base_register_ && - offset_ == our_rhs->offset_); - } - Rule *Copy() const { return new OffsetRule(*this); } - // We don't actually need SetBaseRegister or SetOffset here, since they - // are only ever applied to CFA rules, for DW_CFA_def_cfa_offset, and it - // doesn't make sense to use OffsetRule for computing the CFA: it - // computes the address at which a register is saved, not a value. - private: - int base_register_; - long offset_; -}; - -// Rule: the value the register had in the caller is the value of -// BASE_REGISTER plus offset. BASE_REGISTER may be -// CallFrameInfo::Handler::kCFARegister. -class CallFrameInfo::ValOffsetRule: public CallFrameInfo::Rule { - public: - ValOffsetRule(int base_register, long offset) - : base_register_(base_register), offset_(offset) { } - ~ValOffsetRule() { } - CFIRTag getTag() const { return CFIR_VAL_OFFSET_RULE; } - bool Handle(Handler *handler, uint64 address, int reg) const { - return handler->ValOffsetRule(address, reg, base_register_, offset_); - } - bool operator==(const Rule &rhs) const { - if (rhs.getTag() != CFIR_VAL_OFFSET_RULE) return false; - const ValOffsetRule *our_rhs = static_cast<const ValOffsetRule *>(&rhs); - return (base_register_ == our_rhs->base_register_ && - offset_ == our_rhs->offset_); - } - Rule *Copy() const { return new ValOffsetRule(*this); } - void SetBaseRegister(unsigned reg) { base_register_ = reg; } - void SetOffset(long long offset) { offset_ = offset; } - private: - int base_register_; - long offset_; -}; - -// Rule: the register has been saved in another register REGISTER_NUMBER_. -class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule { - public: - explicit RegisterRule(int register_number) - : register_number_(register_number) { } - ~RegisterRule() { } - CFIRTag getTag() const { return CFIR_REGISTER_RULE; } - bool Handle(Handler *handler, uint64 address, int reg) const { - return handler->RegisterRule(address, reg, register_number_); - } - bool operator==(const Rule &rhs) const { - if (rhs.getTag() != CFIR_REGISTER_RULE) return false; - const RegisterRule *our_rhs = static_cast<const RegisterRule *>(&rhs); - return (register_number_ == our_rhs->register_number_); - } - Rule *Copy() const { return new RegisterRule(*this); } - private: - int register_number_; -}; - -// Rule: EXPRESSION evaluates to the address at which the register is saved. -class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule { - public: - explicit ExpressionRule(const string &expression) - : expression_(expression) { } - ~ExpressionRule() { } - CFIRTag getTag() const { return CFIR_EXPRESSION_RULE; } - bool Handle(Handler *handler, uint64 address, int reg) const { - return handler->ExpressionRule(address, reg, expression_); - } - bool operator==(const Rule &rhs) const { - if (rhs.getTag() != CFIR_EXPRESSION_RULE) return false; - const ExpressionRule *our_rhs = static_cast<const ExpressionRule *>(&rhs); - return (expression_ == our_rhs->expression_); - } - Rule *Copy() const { return new ExpressionRule(*this); } - private: - string expression_; -}; - -// Rule: EXPRESSION evaluates to the previous value of the register. -class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule { - public: - explicit ValExpressionRule(const string &expression) - : expression_(expression) { } - ~ValExpressionRule() { } - CFIRTag getTag() const { return CFIR_VAL_EXPRESSION_RULE; } - bool Handle(Handler *handler, uint64 address, int reg) const { - return handler->ValExpressionRule(address, reg, expression_); - } - bool operator==(const Rule &rhs) const { - if (rhs.getTag() != CFIR_VAL_EXPRESSION_RULE) return false; - const ValExpressionRule *our_rhs = - static_cast<const ValExpressionRule *>(&rhs); - return (expression_ == our_rhs->expression_); - } - Rule *Copy() const { return new ValExpressionRule(*this); } - private: - string expression_; -}; - -// A map from register numbers to rules. -class CallFrameInfo::RuleMap { - public: - RuleMap() : cfa_rule_(NULL) { } - RuleMap(const RuleMap &rhs) : cfa_rule_(NULL) { *this = rhs; } - ~RuleMap() { Clear(); } - - RuleMap &operator=(const RuleMap &rhs); - - // Set the rule for computing the CFA to RULE. Take ownership of RULE. - void SetCFARule(Rule *rule) { delete cfa_rule_; cfa_rule_ = rule; } - - // Return the current CFA rule. Unlike RegisterRule, this RuleMap retains - // ownership of the rule. We use this for DW_CFA_def_cfa_offset and - // DW_CFA_def_cfa_register, and for detecting references to the CFA before - // a rule for it has been established. - Rule *CFARule() const { return cfa_rule_; } - - // Return the rule for REG, or NULL if there is none. The caller takes - // ownership of the result. - Rule *RegisterRule(int reg) const; - - // Set the rule for computing REG to RULE. Take ownership of RULE. - void SetRegisterRule(int reg, Rule *rule); - - // Make all the appropriate calls to HANDLER as if we were changing from - // this RuleMap to NEW_RULES at ADDRESS. We use this to implement - // DW_CFA_restore_state, where lots of rules can change simultaneously. - // Return true if all handlers returned true; otherwise, return false. - bool HandleTransitionTo(Handler *handler, uint64 address, - const RuleMap &new_rules) const; - - private: - // A map from register numbers to Rules. - typedef std::map<int, Rule *> RuleByNumber; - - // Remove all register rules and clear cfa_rule_. - void Clear(); - - // The rule for computing the canonical frame address. This RuleMap owns - // this rule. - Rule *cfa_rule_; - - // A map from register numbers to postfix expressions to recover - // their values. This RuleMap owns the Rules the map refers to. - RuleByNumber registers_; -}; - -CallFrameInfo::RuleMap &CallFrameInfo::RuleMap::operator=(const RuleMap &rhs) { - Clear(); - // Since each map owns the rules it refers to, assignment must copy them. - if (rhs.cfa_rule_) cfa_rule_ = rhs.cfa_rule_->Copy(); - for (RuleByNumber::const_iterator it = rhs.registers_.begin(); - it != rhs.registers_.end(); it++) - registers_[it->first] = it->second->Copy(); - return *this; -} - -CallFrameInfo::Rule *CallFrameInfo::RuleMap::RegisterRule(int reg) const { - MOZ_ASSERT(reg != Handler::kCFARegister); - RuleByNumber::const_iterator it = registers_.find(reg); - if (it != registers_.end()) - return it->second->Copy(); - else - return NULL; -} - -void CallFrameInfo::RuleMap::SetRegisterRule(int reg, Rule *rule) { - MOZ_ASSERT(reg != Handler::kCFARegister); - MOZ_ASSERT(rule); - Rule **slot = ®isters_[reg]; - delete *slot; - *slot = rule; -} - -bool CallFrameInfo::RuleMap::HandleTransitionTo( - Handler *handler, - uint64 address, - const RuleMap &new_rules) const { - // Transition from cfa_rule_ to new_rules.cfa_rule_. - if (cfa_rule_ && new_rules.cfa_rule_) { - if (*cfa_rule_ != *new_rules.cfa_rule_ && - !new_rules.cfa_rule_->Handle(handler, address, Handler::kCFARegister)) - return false; - } else if (cfa_rule_) { - // this RuleMap has a CFA rule but new_rules doesn't. - // CallFrameInfo::Handler has no way to handle this --- and shouldn't; - // it's garbage input. The instruction interpreter should have - // detected this and warned, so take no action here. - } else if (new_rules.cfa_rule_) { - // This shouldn't be possible: NEW_RULES is some prior state, and - // there's no way to remove entries. - MOZ_ASSERT(0); - } else { - // Both CFA rules are empty. No action needed. - } - - // Traverse the two maps in order by register number, and report - // whatever differences we find. - RuleByNumber::const_iterator old_it = registers_.begin(); - RuleByNumber::const_iterator new_it = new_rules.registers_.begin(); - while (old_it != registers_.end() && new_it != new_rules.registers_.end()) { - if (old_it->first < new_it->first) { - // This RuleMap has an entry for old_it->first, but NEW_RULES - // doesn't. - // - // This isn't really the right thing to do, but since CFI generally - // only mentions callee-saves registers, and GCC's convention for - // callee-saves registers is that they are unchanged, it's a good - // approximation. - if (!handler->SameValueRule(address, old_it->first)) - return false; - old_it++; - } else if (old_it->first > new_it->first) { - // NEW_RULES has entry for new_it->first, but this RuleMap - // doesn't. This shouldn't be possible: NEW_RULES is some prior - // state, and there's no way to remove entries. - MOZ_ASSERT(0); - } else { - // Both maps have an entry for this register. Report the new - // rule if it is different. - if (*old_it->second != *new_it->second && - !new_it->second->Handle(handler, address, new_it->first)) - return false; - new_it++, old_it++; - } - } - // Finish off entries from this RuleMap with no counterparts in new_rules. - while (old_it != registers_.end()) { - if (!handler->SameValueRule(address, old_it->first)) - return false; - old_it++; - } - // Since we only make transitions from a rule set to some previously - // saved rule set, and we can only add rules to the map, NEW_RULES - // must have fewer rules than *this. - MOZ_ASSERT(new_it == new_rules.registers_.end()); - - return true; -} - -// Remove all register rules and clear cfa_rule_. -void CallFrameInfo::RuleMap::Clear() { - delete cfa_rule_; - cfa_rule_ = NULL; - for (RuleByNumber::iterator it = registers_.begin(); - it != registers_.end(); it++) - delete it->second; - registers_.clear(); -} - -// The state of the call frame information interpreter as it processes -// instructions from a CIE and FDE. -class CallFrameInfo::State { - public: - // Create a call frame information interpreter state with the given - // reporter, reader, handler, and initial call frame info address. - State(ByteReader *reader, Handler *handler, Reporter *reporter, - uint64 address) - : reader_(reader), handler_(handler), reporter_(reporter), - address_(address), entry_(NULL), cursor_(NULL), - saved_rules_(NULL) { } - - ~State() { - if (saved_rules_) - delete saved_rules_; - } - - // Interpret instructions from CIE, save the resulting rule set for - // DW_CFA_restore instructions, and return true. On error, report - // the problem to reporter_ and return false. - bool InterpretCIE(const CIE &cie); - - // Interpret instructions from FDE, and return true. On error, - // report the problem to reporter_ and return false. - bool InterpretFDE(const FDE &fde); - - private: - // The operands of a CFI instruction, for ParseOperands. - struct Operands { - unsigned register_number; // A register number. - uint64 offset; // An offset or address. - long signed_offset; // A signed offset. - string expression; // A DWARF expression. - }; - - // Parse CFI instruction operands from STATE's instruction stream as - // described by FORMAT. On success, populate OPERANDS with the - // results, and return true. On failure, report the problem and - // return false. - // - // Each character of FORMAT should be one of the following: - // - // 'r' unsigned LEB128 register number (OPERANDS->register_number) - // 'o' unsigned LEB128 offset (OPERANDS->offset) - // 's' signed LEB128 offset (OPERANDS->signed_offset) - // 'a' machine-size address (OPERANDS->offset) - // (If the CIE has a 'z' augmentation string, 'a' uses the - // encoding specified by the 'R' argument.) - // '1' a one-byte offset (OPERANDS->offset) - // '2' a two-byte offset (OPERANDS->offset) - // '4' a four-byte offset (OPERANDS->offset) - // '8' an eight-byte offset (OPERANDS->offset) - // 'e' a DW_FORM_block holding a (OPERANDS->expression) - // DWARF expression - bool ParseOperands(const char *format, Operands *operands); - - // Interpret one CFI instruction from STATE's instruction stream, update - // STATE, report any rule changes to handler_, and return true. On - // failure, report the problem and return false. - bool DoInstruction(); - - // The following Do* member functions are subroutines of DoInstruction, - // factoring out the actual work of operations that have several - // different encodings. - - // Set the CFA rule to be the value of BASE_REGISTER plus OFFSET, and - // return true. On failure, report and return false. (Used for - // DW_CFA_def_cfa and DW_CFA_def_cfa_sf.) - bool DoDefCFA(unsigned base_register, long offset); - - // Change the offset of the CFA rule to OFFSET, and return true. On - // failure, report and return false. (Subroutine for - // DW_CFA_def_cfa_offset and DW_CFA_def_cfa_offset_sf.) - bool DoDefCFAOffset(long offset); - - // Specify that REG can be recovered using RULE, and return true. On - // failure, report and return false. - bool DoRule(unsigned reg, Rule *rule); - - // Specify that REG can be found at OFFSET from the CFA, and return true. - // On failure, report and return false. (Subroutine for DW_CFA_offset, - // DW_CFA_offset_extended, and DW_CFA_offset_extended_sf.) - bool DoOffset(unsigned reg, long offset); - - // Specify that the caller's value for REG is the CFA plus OFFSET, - // and return true. On failure, report and return false. (Subroutine - // for DW_CFA_val_offset and DW_CFA_val_offset_sf.) - bool DoValOffset(unsigned reg, long offset); - - // Restore REG to the rule established in the CIE, and return true. On - // failure, report and return false. (Subroutine for DW_CFA_restore and - // DW_CFA_restore_extended.) - bool DoRestore(unsigned reg); - - // Return the section offset of the instruction at cursor. For use - // in error messages. - uint64 CursorOffset() { return entry_->offset + (cursor_ - entry_->start); } - - // Report that entry_ is incomplete, and return false. For brevity. - bool ReportIncomplete() { - reporter_->Incomplete(entry_->offset, entry_->kind); - return false; - } - - // For reading multi-byte values with the appropriate endianness. - ByteReader *reader_; - - // The handler to which we should report the data we find. - Handler *handler_; - - // For reporting problems in the info we're parsing. - Reporter *reporter_; - - // The code address to which the next instruction in the stream applies. - uint64 address_; - - // The entry whose instructions we are currently processing. This is - // first a CIE, and then an FDE. - const Entry *entry_; - - // The next instruction to process. - const char *cursor_; - - // The current set of rules. - RuleMap rules_; - - // The set of rules established by the CIE, used by DW_CFA_restore - // and DW_CFA_restore_extended. We set this after interpreting the - // CIE's instructions. - RuleMap cie_rules_; - - // A stack of saved states, for DW_CFA_remember_state and - // DW_CFA_restore_state. - std::stack<RuleMap>* saved_rules_; -}; - -bool CallFrameInfo::State::InterpretCIE(const CIE &cie) { - entry_ = &cie; - cursor_ = entry_->instructions; - while (cursor_ < entry_->end) - if (!DoInstruction()) - return false; - // Note the rules established by the CIE, for use by DW_CFA_restore - // and DW_CFA_restore_extended. - cie_rules_ = rules_; - return true; -} - -bool CallFrameInfo::State::InterpretFDE(const FDE &fde) { - entry_ = &fde; - cursor_ = entry_->instructions; - while (cursor_ < entry_->end) - if (!DoInstruction()) - return false; - return true; -} - -bool CallFrameInfo::State::ParseOperands(const char *format, - Operands *operands) { - size_t len; - const char *operand; - - for (operand = format; *operand; operand++) { - size_t bytes_left = entry_->end - cursor_; - switch (*operand) { - case 'r': - operands->register_number = reader_->ReadUnsignedLEB128(cursor_, &len); - if (len > bytes_left) return ReportIncomplete(); - cursor_ += len; - break; - - case 'o': - operands->offset = reader_->ReadUnsignedLEB128(cursor_, &len); - if (len > bytes_left) return ReportIncomplete(); - cursor_ += len; - break; - - case 's': - operands->signed_offset = reader_->ReadSignedLEB128(cursor_, &len); - if (len > bytes_left) return ReportIncomplete(); - cursor_ += len; - break; - - case 'a': - operands->offset = - reader_->ReadEncodedPointer(cursor_, entry_->cie->pointer_encoding, - &len); - if (len > bytes_left) return ReportIncomplete(); - cursor_ += len; - break; - - case '1': - if (1 > bytes_left) return ReportIncomplete(); - operands->offset = static_cast<unsigned char>(*cursor_++); - break; - - case '2': - if (2 > bytes_left) return ReportIncomplete(); - operands->offset = reader_->ReadTwoBytes(cursor_); - cursor_ += 2; - break; - - case '4': - if (4 > bytes_left) return ReportIncomplete(); - operands->offset = reader_->ReadFourBytes(cursor_); - cursor_ += 4; - break; - - case '8': - if (8 > bytes_left) return ReportIncomplete(); - operands->offset = reader_->ReadEightBytes(cursor_); - cursor_ += 8; - break; - - case 'e': { - size_t expression_length = reader_->ReadUnsignedLEB128(cursor_, &len); - if (len > bytes_left || expression_length > bytes_left - len) - return ReportIncomplete(); - cursor_ += len; - operands->expression = string(cursor_, expression_length); - cursor_ += expression_length; - break; - } - - default: - MOZ_ASSERT(0); - } - } - - return true; -} - -bool CallFrameInfo::State::DoInstruction() { - CIE *cie = entry_->cie; - Operands ops; - - // Our entry's kind should have been set by now. - MOZ_ASSERT(entry_->kind != kUnknown); - - // We shouldn't have been invoked unless there were more - // instructions to parse. - MOZ_ASSERT(cursor_ < entry_->end); - - unsigned opcode = *cursor_++; - if ((opcode & 0xc0) != 0) { - switch (opcode & 0xc0) { - // Advance the address. - case DW_CFA_advance_loc: { - size_t code_offset = opcode & 0x3f; - address_ += code_offset * cie->code_alignment_factor; - break; - } - - // Find a register at an offset from the CFA. - case DW_CFA_offset: - if (!ParseOperands("o", &ops) || - !DoOffset(opcode & 0x3f, ops.offset * cie->data_alignment_factor)) - return false; - break; - - // Restore the rule established for a register by the CIE. - case DW_CFA_restore: - if (!DoRestore(opcode & 0x3f)) return false; - break; - - // The 'if' above should have excluded this possibility. - default: - MOZ_ASSERT(0); - } - - // Return here, so the big switch below won't be indented. - return true; - } - - switch (opcode) { - // Set the address. - case DW_CFA_set_loc: - if (!ParseOperands("a", &ops)) return false; - address_ = ops.offset; - break; - - // Advance the address. - case DW_CFA_advance_loc1: - if (!ParseOperands("1", &ops)) return false; - address_ += ops.offset * cie->code_alignment_factor; - break; - - // Advance the address. - case DW_CFA_advance_loc2: - if (!ParseOperands("2", &ops)) return false; - address_ += ops.offset * cie->code_alignment_factor; - break; - - // Advance the address. - case DW_CFA_advance_loc4: - if (!ParseOperands("4", &ops)) return false; - address_ += ops.offset * cie->code_alignment_factor; - break; - - // Advance the address. - case DW_CFA_MIPS_advance_loc8: - if (!ParseOperands("8", &ops)) return false; - address_ += ops.offset * cie->code_alignment_factor; - break; - - // Compute the CFA by adding an offset to a register. - case DW_CFA_def_cfa: - if (!ParseOperands("ro", &ops) || - !DoDefCFA(ops.register_number, ops.offset)) - return false; - break; - - // Compute the CFA by adding an offset to a register. - case DW_CFA_def_cfa_sf: - if (!ParseOperands("rs", &ops) || - !DoDefCFA(ops.register_number, - ops.signed_offset * cie->data_alignment_factor)) - return false; - break; - - // Change the base register used to compute the CFA. - case DW_CFA_def_cfa_register: { - Rule *cfa_rule = rules_.CFARule(); - if (!cfa_rule) { - reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); - return false; - } - if (!ParseOperands("r", &ops)) return false; - cfa_rule->SetBaseRegister(ops.register_number); - if (!cfa_rule->Handle(handler_, address_, Handler::kCFARegister)) - return false; - break; - } - - // Change the offset used to compute the CFA. - case DW_CFA_def_cfa_offset: - if (!ParseOperands("o", &ops) || - !DoDefCFAOffset(ops.offset)) - return false; - break; - - // Change the offset used to compute the CFA. - case DW_CFA_def_cfa_offset_sf: - if (!ParseOperands("s", &ops) || - !DoDefCFAOffset(ops.signed_offset * cie->data_alignment_factor)) - return false; - break; - - // Specify an expression whose value is the CFA. - case DW_CFA_def_cfa_expression: { - if (!ParseOperands("e", &ops)) - return false; - Rule *rule = new ValExpressionRule(ops.expression); - rules_.SetCFARule(rule); - if (!rule->Handle(handler_, address_, Handler::kCFARegister)) - return false; - break; - } - - // The register's value cannot be recovered. - case DW_CFA_undefined: { - if (!ParseOperands("r", &ops) || - !DoRule(ops.register_number, new UndefinedRule())) - return false; - break; - } - - // The register's value is unchanged from its value in the caller. - case DW_CFA_same_value: { - if (!ParseOperands("r", &ops) || - !DoRule(ops.register_number, new SameValueRule())) - return false; - break; - } - - // Find a register at an offset from the CFA. - case DW_CFA_offset_extended: - if (!ParseOperands("ro", &ops) || - !DoOffset(ops.register_number, - ops.offset * cie->data_alignment_factor)) - return false; - break; - - // The register is saved at an offset from the CFA. - case DW_CFA_offset_extended_sf: - if (!ParseOperands("rs", &ops) || - !DoOffset(ops.register_number, - ops.signed_offset * cie->data_alignment_factor)) - return false; - break; - - // The register is saved at an offset from the CFA. - case DW_CFA_GNU_negative_offset_extended: - if (!ParseOperands("ro", &ops) || - !DoOffset(ops.register_number, - -ops.offset * cie->data_alignment_factor)) - return false; - break; - - // The register's value is the sum of the CFA plus an offset. - case DW_CFA_val_offset: - if (!ParseOperands("ro", &ops) || - !DoValOffset(ops.register_number, - ops.offset * cie->data_alignment_factor)) - return false; - break; - - // The register's value is the sum of the CFA plus an offset. - case DW_CFA_val_offset_sf: - if (!ParseOperands("rs", &ops) || - !DoValOffset(ops.register_number, - ops.signed_offset * cie->data_alignment_factor)) - return false; - break; - - // The register has been saved in another register. - case DW_CFA_register: { - if (!ParseOperands("ro", &ops) || - !DoRule(ops.register_number, new RegisterRule(ops.offset))) - return false; - break; - } - - // An expression yields the address at which the register is saved. - case DW_CFA_expression: { - if (!ParseOperands("re", &ops) || - !DoRule(ops.register_number, new ExpressionRule(ops.expression))) - return false; - break; - } - - // An expression yields the caller's value for the register. - case DW_CFA_val_expression: { - if (!ParseOperands("re", &ops) || - !DoRule(ops.register_number, new ValExpressionRule(ops.expression))) - return false; - break; - } - - // Restore the rule established for a register by the CIE. - case DW_CFA_restore_extended: - if (!ParseOperands("r", &ops) || - !DoRestore( ops.register_number)) - return false; - break; - - // Save the current set of rules on a stack. - case DW_CFA_remember_state: - if (!saved_rules_) { - saved_rules_ = new std::stack<RuleMap>(); - } - saved_rules_->push(rules_); - break; - - // Pop the current set of rules off the stack. - case DW_CFA_restore_state: { - if (!saved_rules_ || saved_rules_->empty()) { - reporter_->EmptyStateStack(entry_->offset, entry_->kind, - CursorOffset()); - return false; - } - const RuleMap &new_rules = saved_rules_->top(); - if (rules_.CFARule() && !new_rules.CFARule()) { - reporter_->ClearingCFARule(entry_->offset, entry_->kind, - CursorOffset()); - return false; - } - rules_.HandleTransitionTo(handler_, address_, new_rules); - rules_ = new_rules; - saved_rules_->pop(); - break; - } - - // No operation. (Padding instruction.) - case DW_CFA_nop: - break; - - // A SPARC register window save: Registers 8 through 15 (%o0-%o7) - // are saved in registers 24 through 31 (%i0-%i7), and registers - // 16 through 31 (%l0-%l7 and %i0-%i7) are saved at CFA offsets - // (0-15 * the register size). The register numbers must be - // hard-coded. A GNU extension, and not a pretty one. - case DW_CFA_GNU_window_save: { - // Save %o0-%o7 in %i0-%i7. - for (int i = 8; i < 16; i++) - if (!DoRule(i, new RegisterRule(i + 16))) - return false; - // Save %l0-%l7 and %i0-%i7 at the CFA. - for (int i = 16; i < 32; i++) - // Assume that the byte reader's address size is the same as - // the architecture's register size. !@#%*^ hilarious. - if (!DoRule(i, new OffsetRule(Handler::kCFARegister, - (i - 16) * reader_->AddressSize()))) - return false; - break; - } - - // I'm not sure what this is. GDB doesn't use it for unwinding. - case DW_CFA_GNU_args_size: - if (!ParseOperands("o", &ops)) return false; - break; - - // An opcode we don't recognize. - default: { - reporter_->BadInstruction(entry_->offset, entry_->kind, CursorOffset()); - return false; - } - } - - return true; -} - -bool CallFrameInfo::State::DoDefCFA(unsigned base_register, long offset) { - Rule *rule = new ValOffsetRule(base_register, offset); - rules_.SetCFARule(rule); - return rule->Handle(handler_, address_, Handler::kCFARegister); -} - -bool CallFrameInfo::State::DoDefCFAOffset(long offset) { - Rule *cfa_rule = rules_.CFARule(); - if (!cfa_rule) { - reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); - return false; - } - cfa_rule->SetOffset(offset); - return cfa_rule->Handle(handler_, address_, Handler::kCFARegister); -} - -bool CallFrameInfo::State::DoRule(unsigned reg, Rule *rule) { - rules_.SetRegisterRule(reg, rule); - return rule->Handle(handler_, address_, reg); -} - -bool CallFrameInfo::State::DoOffset(unsigned reg, long offset) { - if (!rules_.CFARule()) { - reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); - return false; - } - return DoRule(reg, - new OffsetRule(Handler::kCFARegister, offset)); -} - -bool CallFrameInfo::State::DoValOffset(unsigned reg, long offset) { - if (!rules_.CFARule()) { - reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); - return false; - } - return DoRule(reg, - new ValOffsetRule(Handler::kCFARegister, offset)); -} - -bool CallFrameInfo::State::DoRestore(unsigned reg) { - // DW_CFA_restore and DW_CFA_restore_extended don't make sense in a CIE. - if (entry_->kind == kCIE) { - reporter_->RestoreInCIE(entry_->offset, CursorOffset()); - return false; - } - Rule *rule = cie_rules_.RegisterRule(reg); - if (!rule) { - // This isn't really the right thing to do, but since CFI generally - // only mentions callee-saves registers, and GCC's convention for - // callee-saves registers is that they are unchanged, it's a good - // approximation. - rule = new SameValueRule(); - } - return DoRule(reg, rule); -} - -bool CallFrameInfo::ReadEntryPrologue(const char *cursor, Entry *entry) { - const char *buffer_end = buffer_ + buffer_length_; - - // Initialize enough of ENTRY for use in error reporting. - entry->offset = cursor - buffer_; - entry->start = cursor; - entry->kind = kUnknown; - entry->end = NULL; - - // Read the initial length. This sets reader_'s offset size. - size_t length_size; - uint64 length = reader_->ReadInitialLength(cursor, &length_size); - if (length_size > size_t(buffer_end - cursor)) - return ReportIncomplete(entry); - cursor += length_size; - - // In a .eh_frame section, a length of zero marks the end of the series - // of entries. - if (length == 0 && eh_frame_) { - entry->kind = kTerminator; - entry->end = cursor; - return true; - } - - // Validate the length. - if (length > size_t(buffer_end - cursor)) - return ReportIncomplete(entry); - - // The length is the number of bytes after the initial length field; - // we have that position handy at this point, so compute the end - // now. (If we're parsing 64-bit-offset DWARF on a 32-bit machine, - // and the length didn't fit in a size_t, we would have rejected it - // above.) - entry->end = cursor + length; - - // Parse the next field: either the offset of a CIE or a CIE id. - size_t offset_size = reader_->OffsetSize(); - if (offset_size > size_t(entry->end - cursor)) return ReportIncomplete(entry); - entry->id = reader_->ReadOffset(cursor); - - // Don't advance cursor past id field yet; in .eh_frame data we need - // the id's position to compute the section offset of an FDE's CIE. - - // Now we can decide what kind of entry this is. - if (eh_frame_) { - // In .eh_frame data, an ID of zero marks the entry as a CIE, and - // anything else is an offset from the id field of the FDE to the start - // of the CIE. - if (entry->id == 0) { - entry->kind = kCIE; - } else { - entry->kind = kFDE; - // Turn the offset from the id into an offset from the buffer's start. - entry->id = (cursor - buffer_) - entry->id; - } - } else { - // In DWARF CFI data, an ID of ~0 (of the appropriate width, given the - // offset size for the entry) marks the entry as a CIE, and anything - // else is the offset of the CIE from the beginning of the section. - if (offset_size == 4) - entry->kind = (entry->id == 0xffffffff) ? kCIE : kFDE; - else { - MOZ_ASSERT(offset_size == 8); - entry->kind = (entry->id == 0xffffffffffffffffULL) ? kCIE : kFDE; - } - } - - // Now advance cursor past the id. - cursor += offset_size; - - // The fields specific to this kind of entry start here. - entry->fields = cursor; - - entry->cie = NULL; - - return true; -} - -bool CallFrameInfo::ReadCIEFields(CIE *cie) { - const char *cursor = cie->fields; - size_t len; - - MOZ_ASSERT(cie->kind == kCIE); - - // Prepare for early exit. - cie->version = 0; - cie->augmentation.clear(); - cie->code_alignment_factor = 0; - cie->data_alignment_factor = 0; - cie->return_address_register = 0; - cie->has_z_augmentation = false; - cie->pointer_encoding = DW_EH_PE_absptr; - cie->instructions = 0; - - // Parse the version number. - if (cie->end - cursor < 1) - return ReportIncomplete(cie); - cie->version = reader_->ReadOneByte(cursor); - cursor++; - - // If we don't recognize the version, we can't parse any more fields of the - // CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a - // version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well; - // the difference between those versions seems to be the same as for - // .debug_frame. - if (cie->version < 1 || cie->version > 3) { - reporter_->UnrecognizedVersion(cie->offset, cie->version); - return false; - } - - const char *augmentation_start = cursor; - const void *augmentation_end = - memchr(augmentation_start, '\0', cie->end - augmentation_start); - if (! augmentation_end) return ReportIncomplete(cie); - cursor = static_cast<const char *>(augmentation_end); - cie->augmentation = string(augmentation_start, - cursor - augmentation_start); - // Skip the terminating '\0'. - cursor++; - - // Is this CFI augmented? - if (!cie->augmentation.empty()) { - // Is it an augmentation we recognize? - if (cie->augmentation[0] == DW_Z_augmentation_start) { - // Linux C++ ABI 'z' augmentation, used for exception handling data. - cie->has_z_augmentation = true; - } else { - // Not an augmentation we recognize. Augmentations can have arbitrary - // effects on the form of rest of the content, so we have to give up. - reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation); - return false; - } - } - - // Parse the code alignment factor. - cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len); - if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); - cursor += len; - - // Parse the data alignment factor. - cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len); - if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); - cursor += len; - - // Parse the return address register. This is a ubyte in version 1, and - // a ULEB128 in version 3. - if (cie->version == 1) { - if (cursor >= cie->end) return ReportIncomplete(cie); - cie->return_address_register = uint8(*cursor++); - } else { - cie->return_address_register = reader_->ReadUnsignedLEB128(cursor, &len); - if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); - cursor += len; - } - - // If we have a 'z' augmentation string, find the augmentation data and - // use the augmentation string to parse it. - if (cie->has_z_augmentation) { - uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &len); - if (size_t(cie->end - cursor) < len + data_size) - return ReportIncomplete(cie); - cursor += len; - const char *data = cursor; - cursor += data_size; - const char *data_end = cursor; - - cie->has_z_lsda = false; - cie->has_z_personality = false; - cie->has_z_signal_frame = false; - - // Walk the augmentation string, and extract values from the - // augmentation data as the string directs. - for (size_t i = 1; i < cie->augmentation.size(); i++) { - switch (cie->augmentation[i]) { - case DW_Z_has_LSDA: - // The CIE's augmentation data holds the language-specific data - // area pointer's encoding, and the FDE's augmentation data holds - // the pointer itself. - cie->has_z_lsda = true; - // Fetch the LSDA encoding from the augmentation data. - if (data >= data_end) return ReportIncomplete(cie); - cie->lsda_encoding = DwarfPointerEncoding(*data++); - if (!reader_->ValidEncoding(cie->lsda_encoding)) { - reporter_->InvalidPointerEncoding(cie->offset, cie->lsda_encoding); - return false; - } - // Don't check if the encoding is usable here --- we haven't - // read the FDE's fields yet, so we're not prepared for - // DW_EH_PE_funcrel, although that's a fine encoding for the - // LSDA to use, since it appears in the FDE. - break; - - case DW_Z_has_personality_routine: - // The CIE's augmentation data holds the personality routine - // pointer's encoding, followed by the pointer itself. - cie->has_z_personality = true; - // Fetch the personality routine pointer's encoding from the - // augmentation data. - if (data >= data_end) return ReportIncomplete(cie); - cie->personality_encoding = DwarfPointerEncoding(*data++); - if (!reader_->ValidEncoding(cie->personality_encoding)) { - reporter_->InvalidPointerEncoding(cie->offset, - cie->personality_encoding); - return false; - } - if (!reader_->UsableEncoding(cie->personality_encoding)) { - reporter_->UnusablePointerEncoding(cie->offset, - cie->personality_encoding); - return false; - } - // Fetch the personality routine's pointer itself from the data. - cie->personality_address = - reader_->ReadEncodedPointer(data, cie->personality_encoding, - &len); - if (len > size_t(data_end - data)) - return ReportIncomplete(cie); - data += len; - break; - - case DW_Z_has_FDE_address_encoding: - // The CIE's augmentation data holds the pointer encoding to use - // for addresses in the FDE. - if (data >= data_end) return ReportIncomplete(cie); - cie->pointer_encoding = DwarfPointerEncoding(*data++); - if (!reader_->ValidEncoding(cie->pointer_encoding)) { - reporter_->InvalidPointerEncoding(cie->offset, - cie->pointer_encoding); - return false; - } - if (!reader_->UsableEncoding(cie->pointer_encoding)) { - reporter_->UnusablePointerEncoding(cie->offset, - cie->pointer_encoding); - return false; - } - break; - - case DW_Z_is_signal_trampoline: - // Frames using this CIE are signal delivery frames. - cie->has_z_signal_frame = true; - break; - - default: - // An augmentation we don't recognize. - reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation); - return false; - } - } - } - - // The CIE's instructions start here. - cie->instructions = cursor; - - return true; -} - -bool CallFrameInfo::ReadFDEFields(FDE *fde) { - const char *cursor = fde->fields; - size_t size; - - fde->address = reader_->ReadEncodedPointer(cursor, fde->cie->pointer_encoding, - &size); - if (size > size_t(fde->end - cursor)) - return ReportIncomplete(fde); - cursor += size; - reader_->SetFunctionBase(fde->address); - - // For the length, we strip off the upper nybble of the encoding used for - // the starting address. - DwarfPointerEncoding length_encoding = - DwarfPointerEncoding(fde->cie->pointer_encoding & 0x0f); - fde->size = reader_->ReadEncodedPointer(cursor, length_encoding, &size); - if (size > size_t(fde->end - cursor)) - return ReportIncomplete(fde); - cursor += size; - - // If the CIE has a 'z' augmentation string, then augmentation data - // appears here. - if (fde->cie->has_z_augmentation) { - uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &size); - if (size_t(fde->end - cursor) < size + data_size) - return ReportIncomplete(fde); - cursor += size; - - // In the abstract, we should walk the augmentation string, and extract - // items from the FDE's augmentation data as we encounter augmentation - // string characters that specify their presence: the ordering of items - // in the augmentation string determines the arrangement of values in - // the augmentation data. - // - // In practice, there's only ever one value in FDE augmentation data - // that we support --- the LSDA pointer --- and we have to bail if we - // see any unrecognized augmentation string characters. So if there is - // anything here at all, we know what it is, and where it starts. - if (fde->cie->has_z_lsda) { - // Check whether the LSDA's pointer encoding is usable now: only once - // we've parsed the FDE's starting address do we call reader_-> - // SetFunctionBase, so that the DW_EH_PE_funcrel encoding becomes - // usable. - if (!reader_->UsableEncoding(fde->cie->lsda_encoding)) { - reporter_->UnusablePointerEncoding(fde->cie->offset, - fde->cie->lsda_encoding); - return false; - } - - fde->lsda_address = - reader_->ReadEncodedPointer(cursor, fde->cie->lsda_encoding, &size); - if (size > data_size) - return ReportIncomplete(fde); - // Ideally, we would also complain here if there were unconsumed - // augmentation data. - } - - cursor += data_size; - } - - // The FDE's instructions start after those. - fde->instructions = cursor; - - return true; -} - -bool CallFrameInfo::Start() { - const char *buffer_end = buffer_ + buffer_length_; - const char *cursor; - bool all_ok = true; - const char *entry_end; - bool ok; - - // Traverse all the entries in buffer_, skipping CIEs and offering - // FDEs to the handler. - for (cursor = buffer_; cursor < buffer_end; - cursor = entry_end, all_ok = all_ok && ok) { - FDE fde; - - // Make it easy to skip this entry with 'continue': assume that - // things are not okay until we've checked all the data, and - // prepare the address of the next entry. - ok = false; - - // Read the entry's prologue. - if (!ReadEntryPrologue(cursor, &fde)) { - if (!fde.end) { - // If we couldn't even figure out this entry's extent, then we - // must stop processing entries altogether. - all_ok = false; - break; - } - entry_end = fde.end; - continue; - } - - // The next iteration picks up after this entry. - entry_end = fde.end; - - // Did we see an .eh_frame terminating mark? - if (fde.kind == kTerminator) { - // If there appears to be more data left in the section after the - // terminating mark, warn the user. But this is just a warning; - // we leave all_ok true. - if (fde.end < buffer_end) reporter_->EarlyEHTerminator(fde.offset); - break; - } - - // In this loop, we skip CIEs. We only parse them fully when we - // parse an FDE that refers to them. This limits our memory - // consumption (beyond the buffer itself) to that needed to - // process the largest single entry. - if (fde.kind != kFDE) { - ok = true; - continue; - } - - // Validate the CIE pointer. - if (fde.id > buffer_length_) { - reporter_->CIEPointerOutOfRange(fde.offset, fde.id); - continue; - } - - CIE cie; - - // Parse this FDE's CIE header. - if (!ReadEntryPrologue(buffer_ + fde.id, &cie)) - continue; - // This had better be an actual CIE. - if (cie.kind != kCIE) { - reporter_->BadCIEId(fde.offset, fde.id); - continue; - } - if (!ReadCIEFields(&cie)) - continue; - - // We now have the values that govern both the CIE and the FDE. - cie.cie = &cie; - fde.cie = &cie; - - // Parse the FDE's header. - if (!ReadFDEFields(&fde)) - continue; - - // Call Entry to ask the consumer if they're interested. - if (!handler_->Entry(fde.offset, fde.address, fde.size, - cie.version, cie.augmentation, - cie.return_address_register)) { - // The handler isn't interested in this entry. That's not an error. - ok = true; - continue; - } - - if (cie.has_z_augmentation) { - // Report the personality routine address, if we have one. - if (cie.has_z_personality) { - if (!handler_ - ->PersonalityRoutine(cie.personality_address, - IsIndirectEncoding(cie.personality_encoding))) - continue; - } - - // Report the language-specific data area address, if we have one. - if (cie.has_z_lsda) { - if (!handler_ - ->LanguageSpecificDataArea(fde.lsda_address, - IsIndirectEncoding(cie.lsda_encoding))) - continue; - } - - // If this is a signal-handling frame, report that. - if (cie.has_z_signal_frame) { - if (!handler_->SignalHandler()) - continue; - } - } - - // Interpret the CIE's instructions, and then the FDE's instructions. - State state(reader_, handler_, reporter_, fde.address); - ok = state.InterpretCIE(cie) && state.InterpretFDE(fde); - - // Tell the ByteReader that the function start address from the - // FDE header is no longer valid. - reader_->ClearFunctionBase(); - - // Report the end of the entry. - handler_->End(); - } - - return all_ok; -} - -const char *CallFrameInfo::KindName(EntryKind kind) { - if (kind == CallFrameInfo::kUnknown) - return "entry"; - else if (kind == CallFrameInfo::kCIE) - return "common information entry"; - else if (kind == CallFrameInfo::kFDE) - return "frame description entry"; - else { - MOZ_ASSERT (kind == CallFrameInfo::kTerminator); - return ".eh_frame sequence terminator"; - } -} - -bool CallFrameInfo::ReportIncomplete(Entry *entry) { - reporter_->Incomplete(entry->offset, entry->kind); - return false; -} - -void CallFrameInfo::Reporter::Incomplete(uint64 offset, - CallFrameInfo::EntryKind kind) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI %s at offset 0x%llx in '%s': entry ends early\n", - filename_.c_str(), CallFrameInfo::KindName(kind), offset, - section_.c_str()); - log_(buf); -} - -void CallFrameInfo::Reporter::EarlyEHTerminator(uint64 offset) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI at offset 0x%llx in '%s': saw end-of-data marker" - " before end of section contents\n", - filename_.c_str(), offset, section_.c_str()); - log_(buf); -} - -void CallFrameInfo::Reporter::CIEPointerOutOfRange(uint64 offset, - uint64 cie_offset) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI frame description entry at offset 0x%llx in '%s':" - " CIE pointer is out of range: 0x%llx\n", - filename_.c_str(), offset, section_.c_str(), cie_offset); - log_(buf); -} - -void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI frame description entry at offset 0x%llx in '%s':" - " CIE pointer does not point to a CIE: 0x%llx\n", - filename_.c_str(), offset, section_.c_str(), cie_offset); - log_(buf); -} - -void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI frame description entry at offset 0x%llx in '%s':" - " CIE specifies unrecognized version: %d\n", - filename_.c_str(), offset, section_.c_str(), version); - log_(buf); -} - -void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64 offset, - const string &aug) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI frame description entry at offset 0x%llx in '%s':" - " CIE specifies unrecognized augmentation: '%s'\n", - filename_.c_str(), offset, section_.c_str(), aug.c_str()); - log_(buf); -} - -void CallFrameInfo::Reporter::InvalidPointerEncoding(uint64 offset, - uint8 encoding) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI common information entry at offset 0x%llx in '%s':" - " 'z' augmentation specifies invalid pointer encoding: " - "0x%02x\n", - filename_.c_str(), offset, section_.c_str(), encoding); - log_(buf); -} - -void CallFrameInfo::Reporter::UnusablePointerEncoding(uint64 offset, - uint8 encoding) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI common information entry at offset 0x%llx in '%s':" - " 'z' augmentation specifies a pointer encoding for which" - " we have no base address: 0x%02x\n", - filename_.c_str(), offset, section_.c_str(), encoding); - log_(buf); -} - -void CallFrameInfo::Reporter::RestoreInCIE(uint64 offset, uint64 insn_offset) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI common information entry at offset 0x%llx in '%s':" - " the DW_CFA_restore instruction at offset 0x%llx" - " cannot be used in a common information entry\n", - filename_.c_str(), offset, section_.c_str(), insn_offset); - log_(buf); -} - -void CallFrameInfo::Reporter::BadInstruction(uint64 offset, - CallFrameInfo::EntryKind kind, - uint64 insn_offset) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI %s at offset 0x%llx in section '%s':" - " the instruction at offset 0x%llx is unrecognized\n", - filename_.c_str(), CallFrameInfo::KindName(kind), - offset, section_.c_str(), insn_offset); - log_(buf); -} - -void CallFrameInfo::Reporter::NoCFARule(uint64 offset, - CallFrameInfo::EntryKind kind, - uint64 insn_offset) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI %s at offset 0x%llx in section '%s':" - " the instruction at offset 0x%llx assumes that a CFA rule " - "has been set, but none has been set\n", - filename_.c_str(), CallFrameInfo::KindName(kind), offset, - section_.c_str(), insn_offset); - log_(buf); -} - -void CallFrameInfo::Reporter::EmptyStateStack(uint64 offset, - CallFrameInfo::EntryKind kind, - uint64 insn_offset) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI %s at offset 0x%llx in section '%s':" - " the DW_CFA_restore_state instruction at offset 0x%llx" - " should pop a saved state from the stack, but the stack " - "is empty\n", - filename_.c_str(), CallFrameInfo::KindName(kind), offset, - section_.c_str(), insn_offset); - log_(buf); -} - -void CallFrameInfo::Reporter::ClearingCFARule(uint64 offset, - CallFrameInfo::EntryKind kind, - uint64 insn_offset) { - char buf[300]; - SprintfLiteral(buf, - "%s: CFI %s at offset 0x%llx in section '%s':" - " the DW_CFA_restore_state instruction at offset 0x%llx" - " would clear the CFA rule in effect\n", - filename_.c_str(), CallFrameInfo::KindName(kind), offset, - section_.c_str(), insn_offset); - log_(buf); -} - - -unsigned int DwarfCFIToModule::RegisterNames::I386() { - /* - 8 "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi", - 3 "$eip", "$eflags", "$unused1", - 8 "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", - 2 "$unused2", "$unused3", - 8 "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", - 8 "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", - 3 "$fcw", "$fsw", "$mxcsr", - 8 "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5", - 2 "$tr", "$ldtr" - */ - return 8 + 3 + 8 + 2 + 8 + 8 + 3 + 8 + 2; -} - -unsigned int DwarfCFIToModule::RegisterNames::X86_64() { - /* - 8 "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp", - 8 "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", - 1 "$rip", - 8 "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", - 8 "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15", - 8 "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", - 8 "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", - 1 "$rflags", - 8 "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2", - 4 "$fs.base", "$gs.base", "$unused3", "$unused4", - 2 "$tr", "$ldtr", - 3 "$mxcsr", "$fcw", "$fsw" - */ - return 8 + 8 + 1 + 8 + 8 + 8 + 8 + 1 + 8 + 4 + 2 + 3; -} - -// Per ARM IHI 0040A, section 3.1 -unsigned int DwarfCFIToModule::RegisterNames::ARM() { - /* - 8 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", - 8 "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", - 8 "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", - 8 "fps", "cpsr", "", "", "", "", "", "", - 8 "", "", "", "", "", "", "", "", - 8 "", "", "", "", "", "", "", "", - 8 "", "", "", "", "", "", "", "", - 8 "", "", "", "", "", "", "", "", - 8 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", - 8 "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", - 8 "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", - 8 "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", - 8 "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7" - */ - return 13 * 8; -} - -// See prototype for comments. -int32_t parseDwarfExpr(Summariser* summ, const ByteReader* reader, - string expr, bool debug, - bool pushCfaAtStart, bool derefAtEnd) -{ - const char* cursor = expr.c_str(); - const char* end1 = cursor + expr.length(); - - char buf[100]; - if (debug) { - SprintfLiteral(buf, "LUL.DW << DwarfExpr, len is %d\n", - (int)(end1 - cursor)); - summ->Log(buf); - } - - // Add a marker for the start of this expression. In it, indicate - // whether or not the CFA should be pushed onto the stack prior to - // evaluation. - int32_t start_ix - = summ->AddPfxInstr(PfxInstr(PX_Start, pushCfaAtStart ? 1 : 0)); - MOZ_ASSERT(start_ix >= 0); - - while (cursor < end1) { - - uint8 opc = reader->ReadOneByte(cursor); - cursor++; - - const char* nm = nullptr; - PfxExprOp pxop = PX_End; - - switch (opc) { - - case DW_OP_lit0 ... DW_OP_lit31: { - int32_t simm32 = (int32_t)(opc - DW_OP_lit0); - if (debug) { - SprintfLiteral(buf, "LUL.DW DW_OP_lit%d\n", (int)simm32); - summ->Log(buf); - } - (void) summ->AddPfxInstr(PfxInstr(PX_SImm32, simm32)); - break; - } - - case DW_OP_breg0 ... DW_OP_breg31: { - size_t len; - int64_t n = reader->ReadSignedLEB128(cursor, &len); - cursor += len; - DW_REG_NUMBER reg = (DW_REG_NUMBER)(opc - DW_OP_breg0); - if (debug) { - SprintfLiteral(buf, "LUL.DW DW_OP_breg%d %lld\n", - (int)reg, (long long int)n); - summ->Log(buf); - } - // PfxInstr only allows a 32 bit signed offset. So we - // must fail if the immediate is out of range. - if (n < INT32_MIN || INT32_MAX < n) - goto fail; - (void) summ->AddPfxInstr(PfxInstr(PX_DwReg, reg)); - (void) summ->AddPfxInstr(PfxInstr(PX_SImm32, (int32_t)n)); - (void) summ->AddPfxInstr(PfxInstr(PX_Add)); - break; - } - - case DW_OP_const4s: { - uint64_t u64 = reader->ReadFourBytes(cursor); - cursor += 4; - // u64 is guaranteed by |ReadFourBytes| to be in the - // range 0 .. FFFFFFFF inclusive. But to be safe: - uint32_t u32 = (uint32_t)(u64 & 0xFFFFFFFF); - int32_t s32 = (int32_t)u32; - if (debug) { - SprintfLiteral(buf, "LUL.DW DW_OP_const4s %d\n", (int)s32); - summ->Log(buf); - } - (void) summ->AddPfxInstr(PfxInstr(PX_SImm32, s32)); - break; - } - - case DW_OP_deref: nm = "deref"; pxop = PX_Deref; goto no_operands; - case DW_OP_and: nm = "and"; pxop = PX_And; goto no_operands; - case DW_OP_plus: nm = "plus"; pxop = PX_Add; goto no_operands; - case DW_OP_minus: nm = "minus"; pxop = PX_Sub; goto no_operands; - case DW_OP_shl: nm = "shl"; pxop = PX_Shl; goto no_operands; - case DW_OP_ge: nm = "ge"; pxop = PX_CmpGES; goto no_operands; - no_operands: - MOZ_ASSERT(nm && pxop != PX_End); - if (debug) { - SprintfLiteral(buf, "LUL.DW DW_OP_%s\n", nm); - summ->Log(buf); - } - (void) summ->AddPfxInstr(PfxInstr(pxop)); - break; - - default: - if (debug) { - SprintfLiteral(buf, "LUL.DW unknown opc %d\n", (int)opc); - summ->Log(buf); - } - goto fail; - - } // switch (opc) - - } // while (cursor < end1) - - MOZ_ASSERT(cursor >= end1); - - if (cursor > end1) { - // We overran the Dwarf expression. Give up. - goto fail; - } - - // For DW_CFA_expression, what the expression denotes is the address - // of where the previous value is located. The caller of this routine - // may therefore request one last dereference before the end marker is - // inserted. - if (derefAtEnd) { - (void) summ->AddPfxInstr(PfxInstr(PX_Deref)); - } - - // Insert an end marker, and declare success. - (void) summ->AddPfxInstr(PfxInstr(PX_End)); - if (debug) { - SprintfLiteral(buf, "LUL.DW conversion of dwarf expression succeeded, " - "ix = %d\n", (int)start_ix); - summ->Log(buf); - summ->Log("LUL.DW >>\n"); - } - return start_ix; - - fail: - if (debug) { - summ->Log("LUL.DW conversion of dwarf expression failed\n"); - summ->Log("LUL.DW >>\n"); - } - return -1; -} - - -bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length, - uint8 version, const string &augmentation, - unsigned return_address) { - if (DEBUG_DWARF) { - char buf[100]; - SprintfLiteral(buf, "LUL.DW DwarfCFIToModule::Entry 0x%llx,+%lld\n", - address, length); - summ_->Log(buf); - } - - summ_->Entry(address, length); - - // If dwarf2reader::CallFrameInfo can handle this version and - // augmentation, then we should be okay with that, so there's no - // need to check them here. - - // Get ready to collect entries. - return_address_ = return_address; - - // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI - // may not establish any rule for .ra if the return address column - // is an ordinary register, and that register holds the return - // address on entry to the function. So establish an initial .ra - // rule citing the return address register. - if (return_address_ < num_dw_regs_) { - summ_->Rule(address, return_address_, NODEREF, return_address, 0); - } - - return true; -} - -const UniqueString* DwarfCFIToModule::RegisterName(int i) { - if (i < 0) { - MOZ_ASSERT(i == kCFARegister); - return usu_->ToUniqueString(".cfa"); - } - unsigned reg = i; - if (reg == return_address_) - return usu_->ToUniqueString(".ra"); - - char buf[30]; - SprintfLiteral(buf, "dwarf_reg_%u", reg); - return usu_->ToUniqueString(buf); -} - -bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) { - reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg)); - // Treat this as a non-fatal error. - return true; -} - -bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) { - if (DEBUG_DWARF) { - char buf[100]; - SprintfLiteral(buf, "LUL.DW 0x%llx: old r%d = Same\n", address, reg); - summ_->Log(buf); - } - // reg + 0 - summ_->Rule(address, reg, NODEREF, reg, 0); - return true; -} - -bool DwarfCFIToModule::OffsetRule(uint64 address, int reg, - int base_register, long offset) { - if (DEBUG_DWARF) { - char buf[100]; - SprintfLiteral(buf, "LUL.DW 0x%llx: old r%d = *(r%d + %ld)\n", - address, reg, base_register, offset); - summ_->Log(buf); - } - // *(base_register + offset) - summ_->Rule(address, reg, DEREF, base_register, offset); - return true; -} - -bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg, - int base_register, long offset) { - if (DEBUG_DWARF) { - char buf[100]; - SprintfLiteral(buf, "LUL.DW 0x%llx: old r%d = r%d + %ld\n", - address, reg, base_register, offset); - summ_->Log(buf); - } - // base_register + offset - summ_->Rule(address, reg, NODEREF, base_register, offset); - return true; -} - -bool DwarfCFIToModule::RegisterRule(uint64 address, int reg, - int base_register) { - if (DEBUG_DWARF) { - char buf[100]; - SprintfLiteral(buf, "LUL.DW 0x%llx: old r%d = r%d\n", - address, reg, base_register); - summ_->Log(buf); - } - // base_register + 0 - summ_->Rule(address, reg, NODEREF, base_register, 0); - return true; -} - -bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg, - const string &expression) -{ - bool debug = !!DEBUG_DWARF; - int32_t start_ix = parseDwarfExpr(summ_, reader_, expression, debug, - true/*pushCfaAtStart*/, - true/*derefAtEnd*/); - if (start_ix >= 0) { - summ_->Rule(address, reg, PFXEXPR, 0, start_ix); - } else { - // Parsing of the Dwarf expression failed. Treat this as a - // non-fatal error, hence return |true| even on this path. - reporter_->ExpressionCouldNotBeSummarised(entry_offset_, RegisterName(reg)); - } - return true; -} - -bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg, - const string &expression) -{ - bool debug = !!DEBUG_DWARF; - int32_t start_ix = parseDwarfExpr(summ_, reader_, expression, debug, - true/*pushCfaAtStart*/, - false/*!derefAtEnd*/); - if (start_ix >= 0) { - summ_->Rule(address, reg, PFXEXPR, 0, start_ix); - } else { - // Parsing of the Dwarf expression failed. Treat this as a - // non-fatal error, hence return |true| even on this path. - reporter_->ExpressionCouldNotBeSummarised(entry_offset_, RegisterName(reg)); - } - return true; -} - -bool DwarfCFIToModule::End() { - //module_->AddStackFrameEntry(entry_); - if (DEBUG_DWARF) { - summ_->Log("LUL.DW DwarfCFIToModule::End()\n"); - } - summ_->End(); - return true; -} - -void DwarfCFIToModule::Reporter::UndefinedNotSupported( - size_t offset, - const UniqueString* reg) { - char buf[300]; - SprintfLiteral(buf, "DwarfCFIToModule::Reporter::UndefinedNotSupported()\n"); - log_(buf); - //BPLOG(INFO) << file_ << ", section '" << section_ - // << "': the call frame entry at offset 0x" - // << std::setbase(16) << offset << std::setbase(10) - // << " sets the rule for register '" << FromUniqueString(reg) - // << "' to 'undefined', but the Breakpad symbol file format cannot " - // << " express this"; -} - -// FIXME: move this somewhere sensible -static bool is_power_of_2(uint64_t n) -{ - int i, nSetBits = 0; - for (i = 0; i < 8*(int)sizeof(n); i++) { - if ((n & ((uint64_t)1) << i) != 0) - nSetBits++; - } - return nSetBits <= 1; -} - -void DwarfCFIToModule::Reporter::ExpressionCouldNotBeSummarised( - size_t offset, - const UniqueString* reg) { - static uint64_t n_complaints = 0; // This isn't threadsafe - n_complaints++; - if (!is_power_of_2(n_complaints)) - return; - char buf[300]; - SprintfLiteral(buf, - "DwarfCFIToModule::Reporter::" - "ExpressionCouldNotBeSummarised(shown %llu times)\n", - (unsigned long long int)n_complaints); - log_(buf); -} - -} // namespace lul diff --git a/tools/profiler/lul/LulDwarfSummariser.cpp b/tools/profiler/lul/LulDwarfSummariser.cpp deleted file mode 100644 index 74c2565df..000000000 --- a/tools/profiler/lul/LulDwarfSummariser.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "LulDwarfSummariser.h" - -#include "mozilla/Assertions.h" - -// Set this to 1 for verbose logging -#define DEBUG_SUMMARISER 0 - -namespace lul { - -// Do |s64|'s lowest 32 bits sign extend back to |s64| itself? -static inline bool fitsIn32Bits(int64 s64) { - return s64 == ((s64 & 0xffffffff) ^ 0x80000000) - 0x80000000; -} - -// Check a LExpr prefix expression, starting at pfxInstrs[start] up to -// the next PX_End instruction, to ensure that: -// * It only mentions registers that are tracked on this target -// * The start point is sane -// If the expression is ok, return NULL. Else return a pointer -// a const char* holding a bit of text describing the problem. -static const char* -checkPfxExpr(const vector<PfxInstr>* pfxInstrs, int64_t start) -{ - size_t nInstrs = pfxInstrs->size(); - if (start < 0 || start >= (ssize_t)nInstrs) { - return "bogus start point"; - } - size_t i; - for (i = start; i < nInstrs; i++) { - PfxInstr pxi = (*pfxInstrs)[i]; - if (pxi.mOpcode == PX_End) - break; - if (pxi.mOpcode == PX_DwReg && - !registerIsTracked((DW_REG_NUMBER)pxi.mOperand)) { - return "uses untracked reg"; - } - } - return nullptr; // success -} - - -Summariser::Summariser(SecMap* aSecMap, uintptr_t aTextBias, - void(*aLog)(const char*)) - : mSecMap(aSecMap) - , mTextBias(aTextBias) - , mLog(aLog) -{ - mCurrAddr = 0; - mMax1Addr = 0; // Gives an empty range. - - // Initialise the running RuleSet to "haven't got a clue" status. - new (&mCurrRules) RuleSet(); -} - -void -Summariser::Entry(uintptr_t aAddress, uintptr_t aLength) -{ - aAddress += mTextBias; - if (DEBUG_SUMMARISER) { - char buf[100]; - SprintfLiteral(buf, - "LUL Entry(%llx, %llu)\n", - (unsigned long long int)aAddress, - (unsigned long long int)aLength); - mLog(buf); - } - // This throws away any previous summary, that is, assumes - // that the previous summary, if any, has been properly finished - // by a call to End(). - mCurrAddr = aAddress; - mMax1Addr = aAddress + aLength; - new (&mCurrRules) RuleSet(); -} - -void -Summariser::Rule(uintptr_t aAddress, int aNewReg, - LExprHow how, int16_t oldReg, int64_t offset) -{ - aAddress += mTextBias; - if (DEBUG_SUMMARISER) { - char buf[100]; - if (how == NODEREF || how == DEREF) { - bool deref = how == DEREF; - SprintfLiteral(buf, - "LUL 0x%llx old-r%d = %sr%d + %lld%s\n", - (unsigned long long int)aAddress, aNewReg, - deref ? "*(" : "", (int)oldReg, (long long int)offset, - deref ? ")" : ""); - } else if (how == PFXEXPR) { - SprintfLiteral(buf, - "LUL 0x%llx old-r%d = pfx-expr-at %lld\n", - (unsigned long long int)aAddress, aNewReg, - (long long int)offset); - } else { - SprintfLiteral(buf, - "LUL 0x%llx old-r%d = (invalid LExpr!)\n", - (unsigned long long int)aAddress, aNewReg); - } - mLog(buf); - } - - if (mCurrAddr < aAddress) { - // Flush the existing summary first. - mCurrRules.mAddr = mCurrAddr; - mCurrRules.mLen = aAddress - mCurrAddr; - mSecMap->AddRuleSet(&mCurrRules); - if (DEBUG_SUMMARISER) { - mLog("LUL "); mCurrRules.Print(mLog); - mLog("\n"); - } - mCurrAddr = aAddress; - } - - // If for some reason summarisation fails, either or both of these - // become non-null and point at constant text describing the - // problem. Using two rather than just one avoids complications of - // having to concatenate two strings to produce a complete error message. - const char* reason1 = nullptr; - const char* reason2 = nullptr; - - // |offset| needs to be a 32 bit value that sign extends to 64 bits - // on a 64 bit target. We will need to incorporate |offset| into - // any LExpr made here. So we may as well check it right now. - if (!fitsIn32Bits(offset)) { - reason1 = "offset not in signed 32-bit range"; - goto cant_summarise; - } - - // FIXME: factor out common parts of the arch-dependent summarisers. - -#if defined(LUL_ARCH_arm) - - // ----------------- arm ----------------- // - - // Now, can we add the rule to our summary? This depends on whether - // the registers and the overall expression are representable. This - // is the heart of the summarisation process. - switch (aNewReg) { - - case DW_REG_CFA: - // This is a rule that defines the CFA. The only forms we - // choose to represent are: r7/11/12/13 + offset. The offset - // must fit into 32 bits since 'uintptr_t' is 32 bit on ARM, - // hence there is no need to check it for overflow. - if (how != NODEREF) { - reason1 = "rule for DW_REG_CFA: invalid |how|"; - goto cant_summarise; - } - switch (oldReg) { - case DW_REG_ARM_R7: case DW_REG_ARM_R11: - case DW_REG_ARM_R12: case DW_REG_ARM_R13: - break; - default: - reason1 = "rule for DW_REG_CFA: invalid |oldReg|"; - goto cant_summarise; - } - mCurrRules.mCfaExpr = LExpr(how, oldReg, offset); - break; - - case DW_REG_ARM_R7: case DW_REG_ARM_R11: case DW_REG_ARM_R12: - case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15: { - // This is a new rule for R7, R11, R12, R13 (SP), R14 (LR) or - // R15 (the return address). - switch (how) { - case NODEREF: case DEREF: - // Check the old register is one we're tracking. - if (!registerIsTracked((DW_REG_NUMBER)oldReg) && - oldReg != DW_REG_CFA) { - reason1 = "rule for R7/11/12/13/14/15: uses untracked reg"; - goto cant_summarise; - } - break; - case PFXEXPR: { - // Check that the prefix expression only mentions tracked registers. - const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs(); - reason2 = checkPfxExpr(pfxInstrs, offset); - if (reason2) { - reason1 = "rule for R7/11/12/13/14/15: "; - goto cant_summarise; - } - break; - } - default: - goto cant_summarise; - } - LExpr expr = LExpr(how, oldReg, offset); - switch (aNewReg) { - case DW_REG_ARM_R7: mCurrRules.mR7expr = expr; break; - case DW_REG_ARM_R11: mCurrRules.mR11expr = expr; break; - case DW_REG_ARM_R12: mCurrRules.mR12expr = expr; break; - case DW_REG_ARM_R13: mCurrRules.mR13expr = expr; break; - case DW_REG_ARM_R14: mCurrRules.mR14expr = expr; break; - case DW_REG_ARM_R15: mCurrRules.mR15expr = expr; break; - default: MOZ_ASSERT(0); - } - break; - } - - default: - // Leave |reason1| and |reason2| unset here. This program point - // is reached so often that it causes a flood of "Can't - // summarise" messages. In any case, we don't really care about - // the fact that this summary would produce a new value for a - // register that we're not tracking. We do on the other hand - // care if the summary's expression *uses* a register that we're - // not tracking. But in that case one of the above failures - // should tell us which. - goto cant_summarise; - } - - // Mark callee-saved registers (r4 .. r11) as unchanged, if there is - // no other information about them. FIXME: do this just once, at - // the point where the ruleset is committed. - if (mCurrRules.mR7expr.mHow == UNKNOWN) { - mCurrRules.mR7expr = LExpr(NODEREF, DW_REG_ARM_R7, 0); - } - if (mCurrRules.mR11expr.mHow == UNKNOWN) { - mCurrRules.mR11expr = LExpr(NODEREF, DW_REG_ARM_R11, 0); - } - if (mCurrRules.mR12expr.mHow == UNKNOWN) { - mCurrRules.mR12expr = LExpr(NODEREF, DW_REG_ARM_R12, 0); - } - - // The old r13 (SP) value before the call is always the same as the - // CFA. - mCurrRules.mR13expr = LExpr(NODEREF, DW_REG_CFA, 0); - - // If there's no information about R15 (the return address), say - // it's a copy of R14 (the link register). - if (mCurrRules.mR15expr.mHow == UNKNOWN) { - mCurrRules.mR15expr = LExpr(NODEREF, DW_REG_ARM_R14, 0); - } - -#elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - - // ---------------- x64/x86 ---------------- // - - // Now, can we add the rule to our summary? This depends on whether - // the registers and the overall expression are representable. This - // is the heart of the summarisation process. - switch (aNewReg) { - - case DW_REG_CFA: - // This is a rule that defines the CFA. The only forms we can - // represent are: = SP+offset or = FP+offset. - if (how != NODEREF) { - reason1 = "rule for DW_REG_CFA: invalid |how|"; - goto cant_summarise; - } - if (oldReg != DW_REG_INTEL_XSP && oldReg != DW_REG_INTEL_XBP) { - reason1 = "rule for DW_REG_CFA: invalid |oldReg|"; - goto cant_summarise; - } - mCurrRules.mCfaExpr = LExpr(how, oldReg, offset); - break; - - case DW_REG_INTEL_XSP: case DW_REG_INTEL_XBP: case DW_REG_INTEL_XIP: { - // This is a new rule for XSP, XBP or XIP (the return address). - switch (how) { - case NODEREF: case DEREF: - // Check the old register is one we're tracking. - if (!registerIsTracked((DW_REG_NUMBER)oldReg) && - oldReg != DW_REG_CFA) { - reason1 = "rule for XSP/XBP/XIP: uses untracked reg"; - goto cant_summarise; - } - break; - case PFXEXPR: { - // Check that the prefix expression only mentions tracked registers. - const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs(); - reason2 = checkPfxExpr(pfxInstrs, offset); - if (reason2) { - reason1 = "rule for XSP/XBP/XIP: "; - goto cant_summarise; - } - break; - } - default: - goto cant_summarise; - } - LExpr expr = LExpr(how, oldReg, offset); - switch (aNewReg) { - case DW_REG_INTEL_XBP: mCurrRules.mXbpExpr = expr; break; - case DW_REG_INTEL_XSP: mCurrRules.mXspExpr = expr; break; - case DW_REG_INTEL_XIP: mCurrRules.mXipExpr = expr; break; - default: MOZ_CRASH("impossible value for aNewReg"); - } - break; - } - - default: - // Leave |reason1| and |reason2| unset here, for the reasons - // explained in the analogous point in the ARM case just above. - goto cant_summarise; - - } - - // On Intel, it seems the old SP value before the call is always the - // same as the CFA. Therefore, in the absence of any other way to - // recover the SP, specify that the CFA should be copied. - if (mCurrRules.mXspExpr.mHow == UNKNOWN) { - mCurrRules.mXspExpr = LExpr(NODEREF, DW_REG_CFA, 0); - } - - // Also, gcc says "Undef" for BP when it is unchanged. - if (mCurrRules.mXbpExpr.mHow == UNKNOWN) { - mCurrRules.mXbpExpr = LExpr(NODEREF, DW_REG_INTEL_XBP, 0); - } - -#else - -# error "Unsupported arch" -#endif - - return; - - cant_summarise: - if (reason1 || reason2) { - char buf[200]; - SprintfLiteral(buf, "LUL can't summarise: " - "SVMA=0x%llx: %s%s, expr=LExpr(%s,%u,%lld)\n", - (unsigned long long int)(aAddress - mTextBias), - reason1 ? reason1 : "", reason2 ? reason2 : "", - NameOf_LExprHow(how), - (unsigned int)oldReg, (long long int)offset); - mLog(buf); - } -} - -uint32_t -Summariser::AddPfxInstr(PfxInstr pfxi) -{ - return mSecMap->AddPfxInstr(pfxi); -} - -void -Summariser::End() -{ - if (DEBUG_SUMMARISER) { - mLog("LUL End\n"); - } - if (mCurrAddr < mMax1Addr) { - mCurrRules.mAddr = mCurrAddr; - mCurrRules.mLen = mMax1Addr - mCurrAddr; - mSecMap->AddRuleSet(&mCurrRules); - if (DEBUG_SUMMARISER) { - mLog("LUL "); mCurrRules.Print(mLog); - mLog("\n"); - } - } -} - -} // namespace lul diff --git a/tools/profiler/lul/LulDwarfSummariser.h b/tools/profiler/lul/LulDwarfSummariser.h deleted file mode 100644 index b41db1ee3..000000000 --- a/tools/profiler/lul/LulDwarfSummariser.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef LulDwarfSummariser_h -#define LulDwarfSummariser_h - -#include "LulMainInt.h" - -namespace lul { - -class Summariser -{ -public: - Summariser(SecMap* aSecMap, uintptr_t aTextBias, void(*aLog)(const char*)); - - virtual void Entry(uintptr_t aAddress, uintptr_t aLength); - virtual void End(); - - // Tell the summariser that the value for |aNewReg| at |aAddress| is - // recovered using the LExpr that can be constructed using the - // components |how|, |oldReg| and |offset|. The summariser will - // inspect the components and may reject them for various reasons, - // but the hope is that it will find them acceptable and record this - // rule permanently. - virtual void Rule(uintptr_t aAddress, int aNewReg, - LExprHow how, int16_t oldReg, int64_t offset); - - virtual uint32_t AddPfxInstr(PfxInstr pfxi); - - // Send output to the logging sink, for debugging. - virtual void Log(const char* str) { mLog(str); } - -private: - // The SecMap in which we park the finished summaries (RuleSets) and - // also any PfxInstrs derived from Dwarf expressions. - SecMap* mSecMap; - - // Running state for the current summary (RuleSet) under construction. - RuleSet mCurrRules; - - // The start of the address range to which the RuleSet under - // construction applies. - uintptr_t mCurrAddr; - - // The highest address, plus one, for which the RuleSet under - // construction could possibly apply. If there are no further - // incoming events then mCurrRules will eventually be emitted - // as-is, for the range mCurrAddr.. mMax1Addr - 1, if that is - // nonempty. - uintptr_t mMax1Addr; - - // The bias value (to add to the SVMAs, to get AVMAs) to be used - // when adding entries into mSecMap. - uintptr_t mTextBias; - - // A logging sink, for debugging. - void (*mLog)(const char* aFmt); -}; - -} // namespace lul - -#endif // LulDwarfSummariser_h diff --git a/tools/profiler/lul/LulElf.cpp b/tools/profiler/lul/LulElf.cpp deleted file mode 100644 index 6f90d5f13..000000000 --- a/tools/profiler/lul/LulElf.cpp +++ /dev/null @@ -1,915 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ - -// Copyright (c) 2006, 2011, 2012 Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> - -// (derived from) -// dump_symbols.cc: implement google_breakpad::WriteSymbolFile: -// Find all the debugging info in a file and dump it as a Breakpad symbol file. -// -// dump_symbols.h: Read debugging information from an ELF file, and write -// it out as a Breakpad symbol file. - -// This file is derived from the following files in -// toolkit/crashreporter/google-breakpad: -// src/common/linux/dump_symbols.cc -// src/common/linux/elfutils.cc -// src/common/linux/file_id.cc - -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <unistd.h> -#include <arpa/inet.h> - -#include <set> -#include <string> -#include <vector> - -#include "mozilla/Assertions.h" -#include "mozilla/Sprintf.h" - -#include "LulPlatformMacros.h" -#include "LulCommonExt.h" -#include "LulDwarfExt.h" -#include "LulElfInt.h" -#include "LulMainInt.h" - - -#if defined(LUL_PLAT_arm_android) && !defined(SHT_ARM_EXIDX) -// bionic and older glibsc don't define it -# define SHT_ARM_EXIDX (SHT_LOPROC + 1) -#endif - - -// This namespace contains helper functions. -namespace { - -using lul::DwarfCFIToModule; -using lul::FindElfSectionByName; -using lul::GetOffset; -using lul::IsValidElf; -using lul::Module; -using lul::UniqueStringUniverse; -using lul::scoped_ptr; -using lul::Summariser; -using std::string; -using std::vector; -using std::set; - -// -// FDWrapper -// -// Wrapper class to make sure opened file is closed. -// -class FDWrapper { - public: - explicit FDWrapper(int fd) : - fd_(fd) {} - ~FDWrapper() { - if (fd_ != -1) - close(fd_); - } - int get() { - return fd_; - } - int release() { - int fd = fd_; - fd_ = -1; - return fd; - } - private: - int fd_; -}; - -// -// MmapWrapper -// -// Wrapper class to make sure mapped regions are unmapped. -// -class MmapWrapper { - public: - MmapWrapper() : is_set_(false), base_(NULL), size_(0){} - ~MmapWrapper() { - if (is_set_ && base_ != NULL) { - MOZ_ASSERT(size_ > 0); - munmap(base_, size_); - } - } - void set(void *mapped_address, size_t mapped_size) { - is_set_ = true; - base_ = mapped_address; - size_ = mapped_size; - } - void release() { - MOZ_ASSERT(is_set_); - is_set_ = false; - base_ = NULL; - size_ = 0; - } - - private: - bool is_set_; - void *base_; - size_t size_; -}; - - -// Set NUM_DW_REGNAMES to be the number of Dwarf register names -// appropriate to the machine architecture given in HEADER. Return -// true on success, or false if HEADER's machine architecture is not -// supported. -template<typename ElfClass> -bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header, - unsigned int* num_dw_regnames) { - switch (elf_header->e_machine) { - case EM_386: - *num_dw_regnames = DwarfCFIToModule::RegisterNames::I386(); - return true; - case EM_ARM: - *num_dw_regnames = DwarfCFIToModule::RegisterNames::ARM(); - return true; - case EM_X86_64: - *num_dw_regnames = DwarfCFIToModule::RegisterNames::X86_64(); - return true; - default: - MOZ_ASSERT(0); - return false; - } -} - -template<typename ElfClass> -bool LoadDwarfCFI(const string& dwarf_filename, - const typename ElfClass::Ehdr* elf_header, - const char* section_name, - const typename ElfClass::Shdr* section, - const bool eh_frame, - const typename ElfClass::Shdr* got_section, - const typename ElfClass::Shdr* text_section, - const bool big_endian, - SecMap* smap, - uintptr_t text_bias, - UniqueStringUniverse* usu, - void (*log)(const char*)) { - // Find the appropriate set of register names for this file's - // architecture. - unsigned int num_dw_regs = 0; - if (!DwarfCFIRegisterNames<ElfClass>(elf_header, &num_dw_regs)) { - fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';" - " cannot convert DWARF call frame information\n", - dwarf_filename.c_str(), elf_header->e_machine); - return false; - } - - const lul::Endianness endianness - = big_endian ? lul::ENDIANNESS_BIG : lul::ENDIANNESS_LITTLE; - - // Find the call frame information and its size. - const char* cfi = - GetOffset<ElfClass, char>(elf_header, section->sh_offset); - size_t cfi_size = section->sh_size; - - // Plug together the parser, handler, and their entourages. - - // Here's a summariser, which will receive the output of the - // parser, create summaries, and add them to |smap|. - Summariser summ(smap, text_bias, log); - - lul::ByteReader reader(endianness); - reader.SetAddressSize(ElfClass::kAddrSize); - - DwarfCFIToModule::Reporter module_reporter(log, dwarf_filename, section_name); - DwarfCFIToModule handler(num_dw_regs, &module_reporter, &reader, usu, &summ); - - // Provide the base addresses for .eh_frame encoded pointers, if - // possible. - reader.SetCFIDataBase(section->sh_addr, cfi); - if (got_section) - reader.SetDataBase(got_section->sh_addr); - if (text_section) - reader.SetTextBase(text_section->sh_addr); - - lul::CallFrameInfo::Reporter dwarf_reporter(log, dwarf_filename, - section_name); - lul::CallFrameInfo parser(cfi, cfi_size, - &reader, &handler, &dwarf_reporter, - eh_frame); - parser.Start(); - - return true; -} - -bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper, - void** elf_header) { - int obj_fd = open(obj_file.c_str(), O_RDONLY); - if (obj_fd < 0) { - fprintf(stderr, "Failed to open ELF file '%s': %s\n", - obj_file.c_str(), strerror(errno)); - return false; - } - FDWrapper obj_fd_wrapper(obj_fd); - struct stat st; - if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) { - fprintf(stderr, "Unable to fstat ELF file '%s': %s\n", - obj_file.c_str(), strerror(errno)); - return false; - } - // Mapping it read-only is good enough. In any case, mapping it - // read-write confuses Valgrind's debuginfo acquire/discard - // heuristics, making it hard to profile the profiler. - void *obj_base = mmap(nullptr, st.st_size, - PROT_READ, MAP_PRIVATE, obj_fd, 0); - if (obj_base == MAP_FAILED) { - fprintf(stderr, "Failed to mmap ELF file '%s': %s\n", - obj_file.c_str(), strerror(errno)); - return false; - } - map_wrapper->set(obj_base, st.st_size); - *elf_header = obj_base; - if (!IsValidElf(*elf_header)) { - fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str()); - return false; - } - return true; -} - -// Get the endianness of ELF_HEADER. If it's invalid, return false. -template<typename ElfClass> -bool ElfEndianness(const typename ElfClass::Ehdr* elf_header, - bool* big_endian) { - if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) { - *big_endian = false; - return true; - } - if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) { - *big_endian = true; - return true; - } - - fprintf(stderr, "bad data encoding in ELF header: %d\n", - elf_header->e_ident[EI_DATA]); - return false; -} - -// -// LoadSymbolsInfo -// -// Holds the state between the two calls to LoadSymbols() in case it's necessary -// to follow the .gnu_debuglink section and load debug information from a -// different file. -// -template<typename ElfClass> -class LoadSymbolsInfo { - public: - typedef typename ElfClass::Addr Addr; - - explicit LoadSymbolsInfo(const vector<string>& dbg_dirs) : - debug_dirs_(dbg_dirs), - has_loading_addr_(false) {} - - // Keeps track of which sections have been loaded so sections don't - // accidentally get loaded twice from two different files. - void LoadedSection(const string §ion) { - if (loaded_sections_.count(section) == 0) { - loaded_sections_.insert(section); - } else { - fprintf(stderr, "Section %s has already been loaded.\n", - section.c_str()); - } - } - - string debuglink_file() const { - return debuglink_file_; - } - - private: - const vector<string>& debug_dirs_; // Directories in which to - // search for the debug ELF file. - - string debuglink_file_; // Full path to the debug ELF file. - - bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid. - - set<string> loaded_sections_; // Tracks the Loaded ELF sections - // between calls to LoadSymbols(). -}; - -// Find the preferred loading address of the binary. -template<typename ElfClass> -typename ElfClass::Addr GetLoadingAddress( - const typename ElfClass::Phdr* program_headers, - int nheader) { - typedef typename ElfClass::Phdr Phdr; - - // For non-PIC executables (e_type == ET_EXEC), the load address is - // the start address of the first PT_LOAD segment. (ELF requires - // the segments to be sorted by load address.) For PIC executables - // and dynamic libraries (e_type == ET_DYN), this address will - // normally be zero. - for (int i = 0; i < nheader; ++i) { - const Phdr& header = program_headers[i]; - if (header.p_type == PT_LOAD) - return header.p_vaddr; - } - return 0; -} - -template<typename ElfClass> -bool LoadSymbols(const string& obj_file, - const bool big_endian, - const typename ElfClass::Ehdr* elf_header, - const bool read_gnu_debug_link, - LoadSymbolsInfo<ElfClass>* info, - SecMap* smap, - void* rx_avma, size_t rx_size, - UniqueStringUniverse* usu, - void (*log)(const char*)) { - typedef typename ElfClass::Phdr Phdr; - typedef typename ElfClass::Shdr Shdr; - - char buf[500]; - SprintfLiteral(buf, "LoadSymbols: BEGIN %s\n", obj_file.c_str()); - buf[sizeof(buf)-1] = 0; - log(buf); - - // This is how the text bias is calculated. - // BEGIN CALCULATE BIAS - uintptr_t loading_addr = GetLoadingAddress<ElfClass>( - GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff), - elf_header->e_phnum); - uintptr_t text_bias = ((uintptr_t)rx_avma) - loading_addr; - SprintfLiteral(buf, - "LoadSymbols: rx_avma=%llx, text_bias=%llx", - (unsigned long long int)(uintptr_t)rx_avma, - (unsigned long long int)text_bias); - buf[sizeof(buf)-1] = 0; - log(buf); - // END CALCULATE BIAS - - const Shdr* sections = - GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); - const Shdr* section_names = sections + elf_header->e_shstrndx; - const char* names = - GetOffset<ElfClass, char>(elf_header, section_names->sh_offset); - const char *names_end = names + section_names->sh_size; - bool found_usable_info = false; - - // Dwarf Call Frame Information (CFI) is actually independent from - // the other DWARF debugging information, and can be used alone. - const Shdr* dwarf_cfi_section = - FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS, - sections, names, names_end, - elf_header->e_shnum); - if (dwarf_cfi_section) { - // Ignore the return value of this function; even without call frame - // information, the other debugging information could be perfectly - // useful. - info->LoadedSection(".debug_frame"); - bool result = - LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".debug_frame", - dwarf_cfi_section, false, 0, 0, big_endian, - smap, text_bias, usu, log); - found_usable_info = found_usable_info || result; - if (result) - log("LoadSymbols: read CFI from .debug_frame"); - } - - // Linux C++ exception handling information can also provide - // unwinding data. - const Shdr* eh_frame_section = - FindElfSectionByName<ElfClass>(".eh_frame", SHT_PROGBITS, - sections, names, names_end, - elf_header->e_shnum); - if (eh_frame_section) { - // Pointers in .eh_frame data may be relative to the base addresses of - // certain sections. Provide those sections if present. - const Shdr* got_section = - FindElfSectionByName<ElfClass>(".got", SHT_PROGBITS, - sections, names, names_end, - elf_header->e_shnum); - const Shdr* text_section = - FindElfSectionByName<ElfClass>(".text", SHT_PROGBITS, - sections, names, names_end, - elf_header->e_shnum); - info->LoadedSection(".eh_frame"); - // As above, ignore the return value of this function. - bool result = - LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".eh_frame", - eh_frame_section, true, - got_section, text_section, big_endian, - smap, text_bias, usu, log); - found_usable_info = found_usable_info || result; - if (result) - log("LoadSymbols: read CFI from .eh_frame"); - } - - SprintfLiteral(buf, "LoadSymbols: END %s\n", obj_file.c_str()); - buf[sizeof(buf)-1] = 0; - log(buf); - - return found_usable_info; -} - -// Return the breakpad symbol file identifier for the architecture of -// ELF_HEADER. -template<typename ElfClass> -const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) { - typedef typename ElfClass::Half Half; - Half arch = elf_header->e_machine; - switch (arch) { - case EM_386: return "x86"; - case EM_ARM: return "arm"; - case EM_MIPS: return "mips"; - case EM_PPC64: return "ppc64"; - case EM_PPC: return "ppc"; - case EM_S390: return "s390"; - case EM_SPARC: return "sparc"; - case EM_SPARCV9: return "sparcv9"; - case EM_X86_64: return "x86_64"; - default: return NULL; - } -} - -// Format the Elf file identifier in IDENTIFIER as a UUID with the -// dashes removed. -string FormatIdentifier(unsigned char identifier[16]) { - char identifier_str[40]; - lul::FileID::ConvertIdentifierToString( - identifier, - identifier_str, - sizeof(identifier_str)); - string id_no_dash; - for (int i = 0; identifier_str[i] != '\0'; ++i) - if (identifier_str[i] != '-') - id_no_dash += identifier_str[i]; - // Add an extra "0" by the end. PDB files on Windows have an 'age' - // number appended to the end of the file identifier; this isn't - // really used or necessary on other platforms, but be consistent. - id_no_dash += '0'; - return id_no_dash; -} - -// Return the non-directory portion of FILENAME: the portion after the -// last slash, or the whole filename if there are no slashes. -string BaseFileName(const string &filename) { - // Lots of copies! basename's behavior is less than ideal. - char *c_filename = strdup(filename.c_str()); - string base = basename(c_filename); - free(c_filename); - return base; -} - -template<typename ElfClass> -bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, - const string& obj_filename, - const vector<string>& debug_dirs, - SecMap* smap, void* rx_avma, size_t rx_size, - UniqueStringUniverse* usu, - void (*log)(const char*)) { - typedef typename ElfClass::Ehdr Ehdr; - - unsigned char identifier[16]; - if (!lul - ::FileID::ElfFileIdentifierFromMappedFile(elf_header, identifier)) { - fprintf(stderr, "%s: unable to generate file identifier\n", - obj_filename.c_str()); - return false; - } - - const char *architecture = ElfArchitecture<ElfClass>(elf_header); - if (!architecture) { - fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", - obj_filename.c_str(), elf_header->e_machine); - return false; - } - - // Figure out what endianness this file is. - bool big_endian; - if (!ElfEndianness<ElfClass>(elf_header, &big_endian)) - return false; - - string name = BaseFileName(obj_filename); - string os = "Linux"; - string id = FormatIdentifier(identifier); - - LoadSymbolsInfo<ElfClass> info(debug_dirs); - if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header, - !debug_dirs.empty(), &info, - smap, rx_avma, rx_size, usu, log)) { - const string debuglink_file = info.debuglink_file(); - if (debuglink_file.empty()) - return false; - - // Load debuglink ELF file. - fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str()); - MmapWrapper debug_map_wrapper; - Ehdr* debug_elf_header = NULL; - if (!LoadELF(debuglink_file, &debug_map_wrapper, - reinterpret_cast<void**>(&debug_elf_header))) - return false; - // Sanity checks to make sure everything matches up. - const char *debug_architecture = - ElfArchitecture<ElfClass>(debug_elf_header); - if (!debug_architecture) { - fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", - debuglink_file.c_str(), debug_elf_header->e_machine); - return false; - } - if (strcmp(architecture, debug_architecture)) { - fprintf(stderr, "%s with ELF machine architecture %s does not match " - "%s with ELF architecture %s\n", - debuglink_file.c_str(), debug_architecture, - obj_filename.c_str(), architecture); - return false; - } - - bool debug_big_endian; - if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian)) - return false; - if (debug_big_endian != big_endian) { - fprintf(stderr, "%s and %s does not match in endianness\n", - obj_filename.c_str(), debuglink_file.c_str()); - return false; - } - - if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian, - debug_elf_header, false, &info, - smap, rx_avma, rx_size, usu, log)) { - return false; - } - } - - return true; -} - -} // namespace (anon) - - -namespace lul { - -bool ReadSymbolDataInternal(const uint8_t* obj_file, - const string& obj_filename, - const vector<string>& debug_dirs, - SecMap* smap, void* rx_avma, size_t rx_size, - UniqueStringUniverse* usu, - void (*log)(const char*)) { - - if (!IsValidElf(obj_file)) { - fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str()); - return false; - } - - int elfclass = ElfClass(obj_file); - if (elfclass == ELFCLASS32) { - return ReadSymbolDataElfClass<ElfClass32>( - reinterpret_cast<const Elf32_Ehdr*>(obj_file), - obj_filename, debug_dirs, smap, rx_avma, rx_size, usu, log); - } - if (elfclass == ELFCLASS64) { - return ReadSymbolDataElfClass<ElfClass64>( - reinterpret_cast<const Elf64_Ehdr*>(obj_file), - obj_filename, debug_dirs, smap, rx_avma, rx_size, usu, log); - } - - return false; -} - -bool ReadSymbolData(const string& obj_file, - const vector<string>& debug_dirs, - SecMap* smap, void* rx_avma, size_t rx_size, - UniqueStringUniverse* usu, - void (*log)(const char*)) { - MmapWrapper map_wrapper; - void* elf_header = NULL; - if (!LoadELF(obj_file, &map_wrapper, &elf_header)) - return false; - - return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header), - obj_file, debug_dirs, - smap, rx_avma, rx_size, usu, log); -} - - -namespace { - -template<typename ElfClass> -void FindElfClassSection(const char *elf_base, - const char *section_name, - typename ElfClass::Word section_type, - const void **section_start, - int *section_size) { - typedef typename ElfClass::Ehdr Ehdr; - typedef typename ElfClass::Shdr Shdr; - - MOZ_ASSERT(elf_base); - MOZ_ASSERT(section_start); - MOZ_ASSERT(section_size); - - MOZ_ASSERT(strncmp(elf_base, ELFMAG, SELFMAG) == 0); - - const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); - MOZ_ASSERT(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); - - const Shdr* sections = - GetOffset<ElfClass,Shdr>(elf_header, elf_header->e_shoff); - const Shdr* section_names = sections + elf_header->e_shstrndx; - const char* names = - GetOffset<ElfClass,char>(elf_header, section_names->sh_offset); - const char *names_end = names + section_names->sh_size; - - const Shdr* section = - FindElfSectionByName<ElfClass>(section_name, section_type, - sections, names, names_end, - elf_header->e_shnum); - - if (section != NULL && section->sh_size > 0) { - *section_start = elf_base + section->sh_offset; - *section_size = section->sh_size; - } -} - -template<typename ElfClass> -void FindElfClassSegment(const char *elf_base, - typename ElfClass::Word segment_type, - const void **segment_start, - int *segment_size) { - typedef typename ElfClass::Ehdr Ehdr; - typedef typename ElfClass::Phdr Phdr; - - MOZ_ASSERT(elf_base); - MOZ_ASSERT(segment_start); - MOZ_ASSERT(segment_size); - - MOZ_ASSERT(strncmp(elf_base, ELFMAG, SELFMAG) == 0); - - const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); - MOZ_ASSERT(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); - - const Phdr* phdrs = - GetOffset<ElfClass,Phdr>(elf_header, elf_header->e_phoff); - - for (int i = 0; i < elf_header->e_phnum; ++i) { - if (phdrs[i].p_type == segment_type) { - *segment_start = elf_base + phdrs[i].p_offset; - *segment_size = phdrs[i].p_filesz; - return; - } - } -} - -} // namespace (anon) - -bool IsValidElf(const void* elf_base) { - return strncmp(reinterpret_cast<const char*>(elf_base), - ELFMAG, SELFMAG) == 0; -} - -int ElfClass(const void* elf_base) { - const ElfW(Ehdr)* elf_header = - reinterpret_cast<const ElfW(Ehdr)*>(elf_base); - - return elf_header->e_ident[EI_CLASS]; -} - -bool FindElfSection(const void *elf_mapped_base, - const char *section_name, - uint32_t section_type, - const void **section_start, - int *section_size, - int *elfclass) { - MOZ_ASSERT(elf_mapped_base); - MOZ_ASSERT(section_start); - MOZ_ASSERT(section_size); - - *section_start = NULL; - *section_size = 0; - - if (!IsValidElf(elf_mapped_base)) - return false; - - int cls = ElfClass(elf_mapped_base); - if (elfclass) { - *elfclass = cls; - } - - const char* elf_base = - static_cast<const char*>(elf_mapped_base); - - if (cls == ELFCLASS32) { - FindElfClassSection<ElfClass32>(elf_base, section_name, section_type, - section_start, section_size); - return *section_start != NULL; - } else if (cls == ELFCLASS64) { - FindElfClassSection<ElfClass64>(elf_base, section_name, section_type, - section_start, section_size); - return *section_start != NULL; - } - - return false; -} - -bool FindElfSegment(const void *elf_mapped_base, - uint32_t segment_type, - const void **segment_start, - int *segment_size, - int *elfclass) { - MOZ_ASSERT(elf_mapped_base); - MOZ_ASSERT(segment_start); - MOZ_ASSERT(segment_size); - - *segment_start = NULL; - *segment_size = 0; - - if (!IsValidElf(elf_mapped_base)) - return false; - - int cls = ElfClass(elf_mapped_base); - if (elfclass) { - *elfclass = cls; - } - - const char* elf_base = - static_cast<const char*>(elf_mapped_base); - - if (cls == ELFCLASS32) { - FindElfClassSegment<ElfClass32>(elf_base, segment_type, - segment_start, segment_size); - return *segment_start != NULL; - } else if (cls == ELFCLASS64) { - FindElfClassSegment<ElfClass64>(elf_base, segment_type, - segment_start, segment_size); - return *segment_start != NULL; - } - - return false; -} - - -// (derived from) -// file_id.cc: Return a unique identifier for a file -// -// See file_id.h for documentation -// - -// ELF note name and desc are 32-bits word padded. -#define NOTE_PADDING(a) ((a + 3) & ~3) - -// These functions are also used inside the crashed process, so be safe -// and use the syscall/libc wrappers instead of direct syscalls or libc. - -template<typename ElfClass> -static bool ElfClassBuildIDNoteIdentifier(const void *section, int length, - uint8_t identifier[kMDGUIDSize]) { - typedef typename ElfClass::Nhdr Nhdr; - - const void* section_end = reinterpret_cast<const char*>(section) + length; - const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section); - while (reinterpret_cast<const void *>(note_header) < section_end) { - if (note_header->n_type == NT_GNU_BUILD_ID) - break; - note_header = reinterpret_cast<const Nhdr*>( - reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) + - NOTE_PADDING(note_header->n_namesz) + - NOTE_PADDING(note_header->n_descsz)); - } - if (reinterpret_cast<const void *>(note_header) >= section_end || - note_header->n_descsz == 0) { - return false; - } - - const char* build_id = reinterpret_cast<const char*>(note_header) + - sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz); - // Copy as many bits of the build ID as will fit - // into the GUID space. - memset(identifier, 0, kMDGUIDSize); - memcpy(identifier, build_id, - std::min(kMDGUIDSize, (size_t)note_header->n_descsz)); - - return true; -} - -// Attempt to locate a .note.gnu.build-id section in an ELF binary -// and copy as many bytes of it as will fit into |identifier|. -static bool FindElfBuildIDNote(const void *elf_mapped_base, - uint8_t identifier[kMDGUIDSize]) { - void* note_section; - int note_size, elfclass; - if ((!FindElfSegment(elf_mapped_base, PT_NOTE, - (const void**)¬e_section, ¬e_size, &elfclass) || - note_size == 0) && - (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE, - (const void**)¬e_section, ¬e_size, &elfclass) || - note_size == 0)) { - return false; - } - - if (elfclass == ELFCLASS32) { - return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, note_size, - identifier); - } else if (elfclass == ELFCLASS64) { - return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, note_size, - identifier); - } - - return false; -} - -// Attempt to locate the .text section of an ELF binary and generate -// a simple hash by XORing the first page worth of bytes into |identifier|. -static bool HashElfTextSection(const void *elf_mapped_base, - uint8_t identifier[kMDGUIDSize]) { - void* text_section; - int text_size; - if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS, - (const void**)&text_section, &text_size, NULL) || - text_size == 0) { - return false; - } - - memset(identifier, 0, kMDGUIDSize); - const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section); - const uint8_t* ptr_end = ptr + std::min(text_size, 4096); - while (ptr < ptr_end) { - for (unsigned i = 0; i < kMDGUIDSize; i++) - identifier[i] ^= ptr[i]; - ptr += kMDGUIDSize; - } - return true; -} - -// static -bool FileID::ElfFileIdentifierFromMappedFile(const void* base, - uint8_t identifier[kMDGUIDSize]) { - // Look for a build id note first. - if (FindElfBuildIDNote(base, identifier)) - return true; - - // Fall back on hashing the first page of the text section. - return HashElfTextSection(base, identifier); -} - -// static -void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], - char* buffer, int buffer_length) { - uint8_t identifier_swapped[kMDGUIDSize]; - - // Endian-ness swap to match dump processor expectation. - memcpy(identifier_swapped, identifier, kMDGUIDSize); - uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped); - *data1 = htonl(*data1); - uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4); - *data2 = htons(*data2); - uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6); - *data3 = htons(*data3); - - int buffer_idx = 0; - for (unsigned int idx = 0; - (buffer_idx < buffer_length) && (idx < kMDGUIDSize); - ++idx) { - int hi = (identifier_swapped[idx] >> 4) & 0x0F; - int lo = (identifier_swapped[idx]) & 0x0F; - - if (idx == 4 || idx == 6 || idx == 8 || idx == 10) - buffer[buffer_idx++] = '-'; - - buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi; - buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo; - } - - // NULL terminate - buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0; -} - -} // namespace lul diff --git a/tools/profiler/lul/LulMain.cpp b/tools/profiler/lul/LulMain.cpp deleted file mode 100644 index 2e78f03ec..000000000 --- a/tools/profiler/lul/LulMain.cpp +++ /dev/null @@ -1,1963 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "LulMain.h" - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> - -#include <algorithm> // std::sort -#include <string> - -#include "mozilla/Assertions.h" -#include "mozilla/ArrayUtils.h" -#include "mozilla/CheckedInt.h" -#include "mozilla/DebugOnly.h" -#include "mozilla/MemoryChecking.h" -#include "mozilla/Sprintf.h" - -#include "LulCommonExt.h" -#include "LulElfExt.h" - -#include "LulMainInt.h" - -#include "platform-linux-lul.h" // for gettid() - -// Set this to 1 for verbose logging -#define DEBUG_MAIN 0 - -namespace lul { - -using std::string; -using std::vector; -using std::pair; -using mozilla::CheckedInt; -using mozilla::DebugOnly; - - -// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING -// -// Some functions in this file are marked RUNS IN NO-MALLOC CONTEXT. -// Any such function -- and, hence, the transitive closure of those -// reachable from it -- must not do any dynamic memory allocation. -// Doing so risks deadlock. There is exactly one root function for -// the transitive closure: Lul::Unwind. -// -// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING - - -//////////////////////////////////////////////////////////////// -// RuleSet // -//////////////////////////////////////////////////////////////// - -static const char* -NameOf_DW_REG(int16_t aReg) -{ - switch (aReg) { - case DW_REG_CFA: return "cfa"; -#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - case DW_REG_INTEL_XBP: return "xbp"; - case DW_REG_INTEL_XSP: return "xsp"; - case DW_REG_INTEL_XIP: return "xip"; -#elif defined(LUL_ARCH_arm) - case DW_REG_ARM_R7: return "r7"; - case DW_REG_ARM_R11: return "r11"; - case DW_REG_ARM_R12: return "r12"; - case DW_REG_ARM_R13: return "r13"; - case DW_REG_ARM_R14: return "r14"; - case DW_REG_ARM_R15: return "r15"; -#else -# error "Unsupported arch" -#endif - default: return "???"; - } -} - -string -LExpr::ShowRule(const char* aNewReg) const -{ - char buf[64]; - string res = string(aNewReg) + "="; - switch (mHow) { - case UNKNOWN: - res += "Unknown"; - break; - case NODEREF: - SprintfLiteral(buf, "%s+%d", - NameOf_DW_REG(mReg), (int)mOffset); - res += buf; - break; - case DEREF: - SprintfLiteral(buf, "*(%s+%d)", - NameOf_DW_REG(mReg), (int)mOffset); - res += buf; - break; - case PFXEXPR: - SprintfLiteral(buf, "PfxExpr-at-%d", (int)mOffset); - res += buf; - break; - default: - res += "???"; - break; - } - return res; -} - -void -RuleSet::Print(void(*aLog)(const char*)) const -{ - char buf[96]; - SprintfLiteral(buf, "[%llx .. %llx]: let ", - (unsigned long long int)mAddr, - (unsigned long long int)(mAddr + mLen - 1)); - string res = string(buf); - res += mCfaExpr.ShowRule("cfa"); - res += " in"; - // For each reg we care about, print the recovery expression. -#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - res += mXipExpr.ShowRule(" RA"); - res += mXspExpr.ShowRule(" SP"); - res += mXbpExpr.ShowRule(" BP"); -#elif defined(LUL_ARCH_arm) - res += mR15expr.ShowRule(" R15"); - res += mR7expr .ShowRule(" R7" ); - res += mR11expr.ShowRule(" R11"); - res += mR12expr.ShowRule(" R12"); - res += mR13expr.ShowRule(" R13"); - res += mR14expr.ShowRule(" R14"); -#else -# error "Unsupported arch" -#endif - aLog(res.c_str()); -} - -LExpr* -RuleSet::ExprForRegno(DW_REG_NUMBER aRegno) { - switch (aRegno) { - case DW_REG_CFA: return &mCfaExpr; -# if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - case DW_REG_INTEL_XIP: return &mXipExpr; - case DW_REG_INTEL_XSP: return &mXspExpr; - case DW_REG_INTEL_XBP: return &mXbpExpr; -# elif defined(LUL_ARCH_arm) - case DW_REG_ARM_R15: return &mR15expr; - case DW_REG_ARM_R14: return &mR14expr; - case DW_REG_ARM_R13: return &mR13expr; - case DW_REG_ARM_R12: return &mR12expr; - case DW_REG_ARM_R11: return &mR11expr; - case DW_REG_ARM_R7: return &mR7expr; -# else -# error "Unknown arch" -# endif - default: return nullptr; - } -} - -RuleSet::RuleSet() -{ - mAddr = 0; - mLen = 0; - // The only other fields are of type LExpr and those are initialised - // by LExpr::LExpr(). -} - - -//////////////////////////////////////////////////////////////// -// SecMap // -//////////////////////////////////////////////////////////////// - -// See header file LulMainInt.h for comments about invariants. - -SecMap::SecMap(void(*aLog)(const char*)) - : mSummaryMinAddr(1) - , mSummaryMaxAddr(0) - , mUsable(true) - , mLog(aLog) -{} - -SecMap::~SecMap() { - mRuleSets.clear(); -} - -// RUNS IN NO-MALLOC CONTEXT -RuleSet* -SecMap::FindRuleSet(uintptr_t ia) { - // Binary search mRuleSets to find one that brackets |ia|. - // lo and hi need to be signed, else the loop termination tests - // don't work properly. Note that this works correctly even when - // mRuleSets.size() == 0. - - // Can't do this until the array has been sorted and preened. - MOZ_ASSERT(mUsable); - - long int lo = 0; - long int hi = (long int)mRuleSets.size() - 1; - while (true) { - // current unsearched space is from lo to hi, inclusive. - if (lo > hi) { - // not found - return nullptr; - } - long int mid = lo + ((hi - lo) / 2); - RuleSet* mid_ruleSet = &mRuleSets[mid]; - uintptr_t mid_minAddr = mid_ruleSet->mAddr; - uintptr_t mid_maxAddr = mid_minAddr + mid_ruleSet->mLen - 1; - if (ia < mid_minAddr) { hi = mid-1; continue; } - if (ia > mid_maxAddr) { lo = mid+1; continue; } - MOZ_ASSERT(mid_minAddr <= ia && ia <= mid_maxAddr); - return mid_ruleSet; - } - // NOTREACHED -} - -// Add a RuleSet to the collection. The rule is copied in. Calling -// this makes the map non-searchable. -void -SecMap::AddRuleSet(const RuleSet* rs) { - mUsable = false; - mRuleSets.push_back(*rs); -} - -// Add a PfxInstr to the vector of such instrs, and return the index -// in the vector. Calling this makes the map non-searchable. -uint32_t -SecMap::AddPfxInstr(PfxInstr pfxi) { - mUsable = false; - mPfxInstrs.push_back(pfxi); - return mPfxInstrs.size() - 1; -} - - -static bool -CmpRuleSetsByAddrLE(const RuleSet& rs1, const RuleSet& rs2) { - return rs1.mAddr < rs2.mAddr; -} - -// Prepare the map for searching. Completely remove any which don't -// fall inside the specified range [start, +len). -void -SecMap::PrepareRuleSets(uintptr_t aStart, size_t aLen) -{ - if (mRuleSets.empty()) { - return; - } - - MOZ_ASSERT(aLen > 0); - if (aLen == 0) { - // This should never happen. - mRuleSets.clear(); - return; - } - - // Sort by start addresses. - std::sort(mRuleSets.begin(), mRuleSets.end(), CmpRuleSetsByAddrLE); - - // Detect any entry not completely contained within [start, +len). - // Set its length to zero, so that the next pass will remove it. - for (size_t i = 0; i < mRuleSets.size(); ++i) { - RuleSet* rs = &mRuleSets[i]; - if (rs->mLen > 0 && - (rs->mAddr < aStart || rs->mAddr + rs->mLen > aStart + aLen)) { - rs->mLen = 0; - } - } - - // Iteratively truncate any overlaps and remove any zero length - // entries that might result, or that may have been present - // initially. Unless the input is seriously screwy, this is - // expected to iterate only once. - while (true) { - size_t i; - size_t n = mRuleSets.size(); - size_t nZeroLen = 0; - - if (n == 0) { - break; - } - - for (i = 1; i < n; ++i) { - RuleSet* prev = &mRuleSets[i-1]; - RuleSet* here = &mRuleSets[i]; - MOZ_ASSERT(prev->mAddr <= here->mAddr); - if (prev->mAddr + prev->mLen > here->mAddr) { - prev->mLen = here->mAddr - prev->mAddr; - } - if (prev->mLen == 0) - nZeroLen++; - } - - if (mRuleSets[n-1].mLen == 0) { - nZeroLen++; - } - - // At this point, the entries are in-order and non-overlapping. - // If none of them are zero-length, we are done. - if (nZeroLen == 0) { - break; - } - - // Slide back the entries to remove the zero length ones. - size_t j = 0; // The write-point. - for (i = 0; i < n; ++i) { - if (mRuleSets[i].mLen == 0) { - continue; - } - if (j != i) mRuleSets[j] = mRuleSets[i]; - ++j; - } - MOZ_ASSERT(i == n); - MOZ_ASSERT(nZeroLen <= n); - MOZ_ASSERT(j == n - nZeroLen); - while (nZeroLen > 0) { - mRuleSets.pop_back(); - nZeroLen--; - } - - MOZ_ASSERT(mRuleSets.size() == j); - } - - size_t n = mRuleSets.size(); - -#ifdef DEBUG - // Do a final check on the rules: their address ranges must be - // ascending, non overlapping, non zero sized. - if (n > 0) { - MOZ_ASSERT(mRuleSets[0].mLen > 0); - for (size_t i = 1; i < n; ++i) { - RuleSet* prev = &mRuleSets[i-1]; - RuleSet* here = &mRuleSets[i]; - MOZ_ASSERT(prev->mAddr < here->mAddr); - MOZ_ASSERT(here->mLen > 0); - MOZ_ASSERT(prev->mAddr + prev->mLen <= here->mAddr); - } - } -#endif - - // Set the summary min and max address values. - if (n == 0) { - // Use the values defined in comments in the class declaration. - mSummaryMinAddr = 1; - mSummaryMaxAddr = 0; - } else { - mSummaryMinAddr = mRuleSets[0].mAddr; - mSummaryMaxAddr = mRuleSets[n-1].mAddr + mRuleSets[n-1].mLen - 1; - } - char buf[150]; - SprintfLiteral(buf, - "PrepareRuleSets: %d entries, smin/smax 0x%llx, 0x%llx\n", - (int)n, (unsigned long long int)mSummaryMinAddr, - (unsigned long long int)mSummaryMaxAddr); - buf[sizeof(buf)-1] = 0; - mLog(buf); - - // Is now usable for binary search. - mUsable = true; - - if (0) { - mLog("\nRulesets after preening\n"); - for (size_t i = 0; i < mRuleSets.size(); ++i) { - mRuleSets[i].Print(mLog); - mLog("\n"); - } - mLog("\n"); - } -} - -bool SecMap::IsEmpty() { - return mRuleSets.empty(); -} - - -//////////////////////////////////////////////////////////////// -// SegArray // -//////////////////////////////////////////////////////////////// - -// A SegArray holds a set of address ranges that together exactly -// cover an address range, with no overlaps or holes. Each range has -// an associated value, which in this case has been specialised to be -// a simple boolean. The representation is kept to minimal canonical -// form in which adjacent ranges with the same associated value are -// merged together. Each range is represented by a |struct Seg|. -// -// SegArrays are used to keep track of which parts of the address -// space are known to contain instructions. -class SegArray { - - public: - void add(uintptr_t lo, uintptr_t hi, bool val) { - if (lo > hi) { - return; - } - split_at(lo); - if (hi < UINTPTR_MAX) { - split_at(hi+1); - } - std::vector<Seg>::size_type iLo, iHi, i; - iLo = find(lo); - iHi = find(hi); - for (i = iLo; i <= iHi; ++i) { - mSegs[i].val = val; - } - preen(); - } - - // RUNS IN NO-MALLOC CONTEXT - bool getBoundingCodeSegment(/*OUT*/uintptr_t* rx_min, - /*OUT*/uintptr_t* rx_max, uintptr_t addr) { - std::vector<Seg>::size_type i = find(addr); - if (!mSegs[i].val) { - return false; - } - *rx_min = mSegs[i].lo; - *rx_max = mSegs[i].hi; - return true; - } - - SegArray() { - Seg s(0, UINTPTR_MAX, false); - mSegs.push_back(s); - } - - private: - struct Seg { - Seg(uintptr_t lo, uintptr_t hi, bool val) : lo(lo), hi(hi), val(val) {} - uintptr_t lo; - uintptr_t hi; - bool val; - }; - - void preen() { - for (std::vector<Seg>::iterator iter = mSegs.begin(); - iter < mSegs.end()-1; - ++iter) { - if (iter[0].val != iter[1].val) { - continue; - } - iter[0].hi = iter[1].hi; - mSegs.erase(iter+1); - // Back up one, so as not to miss an opportunity to merge - // with the entry after this one. - --iter; - } - } - - // RUNS IN NO-MALLOC CONTEXT - std::vector<Seg>::size_type find(uintptr_t a) { - long int lo = 0; - long int hi = (long int)mSegs.size(); - while (true) { - // The unsearched space is lo .. hi inclusive. - if (lo > hi) { - // Not found. This can't happen. - return (std::vector<Seg>::size_type)(-1); - } - long int mid = lo + ((hi - lo) / 2); - uintptr_t mid_lo = mSegs[mid].lo; - uintptr_t mid_hi = mSegs[mid].hi; - if (a < mid_lo) { hi = mid-1; continue; } - if (a > mid_hi) { lo = mid+1; continue; } - return (std::vector<Seg>::size_type)mid; - } - } - - void split_at(uintptr_t a) { - std::vector<Seg>::size_type i = find(a); - if (mSegs[i].lo == a) { - return; - } - mSegs.insert( mSegs.begin()+i+1, mSegs[i] ); - mSegs[i].hi = a-1; - mSegs[i+1].lo = a; - } - - void show() { - printf("<< %d entries:\n", (int)mSegs.size()); - for (std::vector<Seg>::iterator iter = mSegs.begin(); - iter < mSegs.end(); - ++iter) { - printf(" %016llx %016llx %s\n", - (unsigned long long int)(*iter).lo, - (unsigned long long int)(*iter).hi, - (*iter).val ? "true" : "false"); - } - printf(">>\n"); - } - - std::vector<Seg> mSegs; -}; - - -//////////////////////////////////////////////////////////////// -// PriMap // -//////////////////////////////////////////////////////////////// - -class PriMap { - public: - explicit PriMap(void (*aLog)(const char*)) - : mLog(aLog) - {} - - ~PriMap() { - for (std::vector<SecMap*>::iterator iter = mSecMaps.begin(); - iter != mSecMaps.end(); - ++iter) { - delete *iter; - } - mSecMaps.clear(); - } - - // RUNS IN NO-MALLOC CONTEXT - pair<const RuleSet*, const vector<PfxInstr>*> - Lookup(uintptr_t ia) - { - SecMap* sm = FindSecMap(ia); - return pair<const RuleSet*, const vector<PfxInstr>*> - (sm ? sm->FindRuleSet(ia) : nullptr, - sm ? sm->GetPfxInstrs() : nullptr); - } - - // Add a secondary map. No overlaps allowed w.r.t. existing - // secondary maps. - void AddSecMap(SecMap* aSecMap) { - // We can't add an empty SecMap to the PriMap. But that's OK - // since we'd never be able to find anything in it anyway. - if (aSecMap->IsEmpty()) { - return; - } - - // Iterate through the SecMaps and find the right place for this - // one. At the same time, ensure that the in-order - // non-overlapping invariant is preserved (and, generally, holds). - // FIXME: this gives a cost that is O(N^2) in the total number of - // shared objects in the system. ToDo: better. - MOZ_ASSERT(aSecMap->mSummaryMinAddr <= aSecMap->mSummaryMaxAddr); - - size_t num_secMaps = mSecMaps.size(); - uintptr_t i; - for (i = 0; i < num_secMaps; ++i) { - SecMap* sm_i = mSecMaps[i]; - MOZ_ASSERT(sm_i->mSummaryMinAddr <= sm_i->mSummaryMaxAddr); - if (aSecMap->mSummaryMinAddr < sm_i->mSummaryMaxAddr) { - // |aSecMap| needs to be inserted immediately before mSecMaps[i]. - break; - } - } - MOZ_ASSERT(i <= num_secMaps); - if (i == num_secMaps) { - // It goes at the end. - mSecMaps.push_back(aSecMap); - } else { - std::vector<SecMap*>::iterator iter = mSecMaps.begin() + i; - mSecMaps.insert(iter, aSecMap); - } - char buf[100]; - SprintfLiteral(buf, "AddSecMap: now have %d SecMaps\n", - (int)mSecMaps.size()); - buf[sizeof(buf)-1] = 0; - mLog(buf); - } - - // Remove and delete any SecMaps in the mapping, that intersect - // with the specified address range. - void RemoveSecMapsInRange(uintptr_t avma_min, uintptr_t avma_max) { - MOZ_ASSERT(avma_min <= avma_max); - size_t num_secMaps = mSecMaps.size(); - if (num_secMaps > 0) { - intptr_t i; - // Iterate from end to start over the vector, so as to ensure - // that the special case where |avma_min| and |avma_max| denote - // the entire address space, can be completed in time proportional - // to the number of elements in the map. - for (i = (intptr_t)num_secMaps-1; i >= 0; i--) { - SecMap* sm_i = mSecMaps[i]; - if (sm_i->mSummaryMaxAddr < avma_min || - avma_max < sm_i->mSummaryMinAddr) { - // There's no overlap. Move on. - continue; - } - // We need to remove mSecMaps[i] and slide all those above it - // downwards to cover the hole. - mSecMaps.erase(mSecMaps.begin() + i); - delete sm_i; - } - } - } - - // Return the number of currently contained SecMaps. - size_t CountSecMaps() { - return mSecMaps.size(); - } - - // Assess heuristically whether the given address is an instruction - // immediately following a call instruction. - // RUNS IN NO-MALLOC CONTEXT - bool MaybeIsReturnPoint(TaggedUWord aInstrAddr, SegArray* aSegArray) { - if (!aInstrAddr.Valid()) { - return false; - } - - uintptr_t ia = aInstrAddr.Value(); - - // Assume that nobody would be crazy enough to put code in the - // first or last page. - if (ia < 4096 || ((uintptr_t)(-ia)) < 4096) { - return false; - } - - // See if it falls inside a known r-x mapped area. Poking around - // outside such places risks segfaulting. - uintptr_t insns_min, insns_max; - bool b = aSegArray->getBoundingCodeSegment(&insns_min, &insns_max, ia); - if (!b) { - // no code (that we know about) at this address - return false; - } - - // |ia| falls within an r-x range. So we can - // safely poke around in [insns_min, insns_max]. - -#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - // Is the previous instruction recognisably a CALL? This is - // common for the 32- and 64-bit versions, except for the - // simm32(%rip) case, which is 64-bit only. - // - // For all other cases, the 64 bit versions are either identical - // to the 32 bit versions, or have an optional extra leading REX.W - // byte (0x41). Since the extra 0x41 is optional we have to - // ignore it, with the convenient result that the same matching - // logic works for both 32- and 64-bit cases. - - uint8_t* p = (uint8_t*)ia; -# if defined(LUL_ARCH_x64) - // CALL simm32(%rip) == FF15 simm32 - if (ia - 6 >= insns_min && p[-6] == 0xFF && p[-5] == 0x15) { - return true; - } -# endif - // CALL rel32 == E8 rel32 (both 32- and 64-bit) - if (ia - 5 >= insns_min && p[-5] == 0xE8) { - return true; - } - // CALL *%eax .. CALL *%edi == FFD0 .. FFD7 (32-bit) - // CALL *%rax .. CALL *%rdi == FFD0 .. FFD7 (64-bit) - // CALL *%r8 .. CALL *%r15 == 41FFD0 .. 41FFD7 (64-bit) - if (ia - 2 >= insns_min && - p[-2] == 0xFF && p[-1] >= 0xD0 && p[-1] <= 0xD7) { - return true; - } - // Almost all of the remaining cases that occur in practice are - // of the form CALL *simm8(reg) or CALL *simm32(reg). - // - // 64 bit cases: - // - // call *simm8(%rax) FF50 simm8 - // call *simm8(%rcx) FF51 simm8 - // call *simm8(%rdx) FF52 simm8 - // call *simm8(%rbx) FF53 simm8 - // call *simm8(%rsp) FF5424 simm8 - // call *simm8(%rbp) FF55 simm8 - // call *simm8(%rsi) FF56 simm8 - // call *simm8(%rdi) FF57 simm8 - // - // call *simm8(%r8) 41FF50 simm8 - // call *simm8(%r9) 41FF51 simm8 - // call *simm8(%r10) 41FF52 simm8 - // call *simm8(%r11) 41FF53 simm8 - // call *simm8(%r12) 41FF5424 simm8 - // call *simm8(%r13) 41FF55 simm8 - // call *simm8(%r14) 41FF56 simm8 - // call *simm8(%r15) 41FF57 simm8 - // - // call *simm32(%rax) FF90 simm32 - // call *simm32(%rcx) FF91 simm32 - // call *simm32(%rdx) FF92 simm32 - // call *simm32(%rbx) FF93 simm32 - // call *simm32(%rsp) FF9424 simm32 - // call *simm32(%rbp) FF95 simm32 - // call *simm32(%rsi) FF96 simm32 - // call *simm32(%rdi) FF97 simm32 - // - // call *simm32(%r8) 41FF90 simm32 - // call *simm32(%r9) 41FF91 simm32 - // call *simm32(%r10) 41FF92 simm32 - // call *simm32(%r11) 41FF93 simm32 - // call *simm32(%r12) 41FF9424 simm32 - // call *simm32(%r13) 41FF95 simm32 - // call *simm32(%r14) 41FF96 simm32 - // call *simm32(%r15) 41FF97 simm32 - // - // 32 bit cases: - // - // call *simm8(%eax) FF50 simm8 - // call *simm8(%ecx) FF51 simm8 - // call *simm8(%edx) FF52 simm8 - // call *simm8(%ebx) FF53 simm8 - // call *simm8(%esp) FF5424 simm8 - // call *simm8(%ebp) FF55 simm8 - // call *simm8(%esi) FF56 simm8 - // call *simm8(%edi) FF57 simm8 - // - // call *simm32(%eax) FF90 simm32 - // call *simm32(%ecx) FF91 simm32 - // call *simm32(%edx) FF92 simm32 - // call *simm32(%ebx) FF93 simm32 - // call *simm32(%esp) FF9424 simm32 - // call *simm32(%ebp) FF95 simm32 - // call *simm32(%esi) FF96 simm32 - // call *simm32(%edi) FF97 simm32 - if (ia - 3 >= insns_min && - p[-3] == 0xFF && - (p[-2] >= 0x50 && p[-2] <= 0x57 && p[-2] != 0x54)) { - // imm8 case, not including %esp/%rsp - return true; - } - if (ia - 4 >= insns_min && - p[-4] == 0xFF && p[-3] == 0x54 && p[-2] == 0x24) { - // imm8 case for %esp/%rsp - return true; - } - if (ia - 6 >= insns_min && - p[-6] == 0xFF && - (p[-5] >= 0x90 && p[-5] <= 0x97 && p[-5] != 0x94)) { - // imm32 case, not including %esp/%rsp - return true; - } - if (ia - 7 >= insns_min && - p[-7] == 0xFF && p[-6] == 0x94 && p[-5] == 0x24) { - // imm32 case for %esp/%rsp - return true; - } - -#elif defined(LUL_ARCH_arm) - if (ia & 1) { - uint16_t w0 = 0, w1 = 0; - // The return address has its lowest bit set, indicating a return - // to Thumb code. - ia &= ~(uintptr_t)1; - if (ia - 2 >= insns_min && ia - 1 <= insns_max) { - w1 = *(uint16_t*)(ia - 2); - } - if (ia - 4 >= insns_min && ia - 1 <= insns_max) { - w0 = *(uint16_t*)(ia - 4); - } - // Is it a 32-bit Thumb call insn? - // BL simm26 (Encoding T1) - if ((w0 & 0xF800) == 0xF000 && (w1 & 0xC000) == 0xC000) { - return true; - } - // BLX simm26 (Encoding T2) - if ((w0 & 0xF800) == 0xF000 && (w1 & 0xC000) == 0xC000) { - return true; - } - // Other possible cases: - // (BLX Rm, Encoding T1). - // BLX Rm (encoding T1, 16 bit, inspect w1 and ignore w0.) - // 0100 0111 1 Rm 000 - } else { - // Returning to ARM code. - uint32_t a0 = 0; - if ((ia & 3) == 0 && ia - 4 >= insns_min && ia - 1 <= insns_max) { - a0 = *(uint32_t*)(ia - 4); - } - // Leading E forces unconditional only -- fix. It could be - // anything except F, which is the deprecated NV code. - // BL simm26 (Encoding A1) - if ((a0 & 0xFF000000) == 0xEB000000) { - return true; - } - // Other possible cases: - // BLX simm26 (Encoding A2) - //if ((a0 & 0xFE000000) == 0xFA000000) - // return true; - // BLX (register) (A1): BLX <c> <Rm> - // cond 0001 0010 1111 1111 1111 0011 Rm - // again, cond can be anything except NV (0xF) - } - -#else -# error "Unsupported arch" -#endif - - // Not an insn we recognise. - return false; - } - - private: - // RUNS IN NO-MALLOC CONTEXT - SecMap* FindSecMap(uintptr_t ia) { - // Binary search mSecMaps to find one that brackets |ia|. - // lo and hi need to be signed, else the loop termination tests - // don't work properly. - long int lo = 0; - long int hi = (long int)mSecMaps.size() - 1; - while (true) { - // current unsearched space is from lo to hi, inclusive. - if (lo > hi) { - // not found - return nullptr; - } - long int mid = lo + ((hi - lo) / 2); - SecMap* mid_secMap = mSecMaps[mid]; - uintptr_t mid_minAddr = mid_secMap->mSummaryMinAddr; - uintptr_t mid_maxAddr = mid_secMap->mSummaryMaxAddr; - if (ia < mid_minAddr) { hi = mid-1; continue; } - if (ia > mid_maxAddr) { lo = mid+1; continue; } - MOZ_ASSERT(mid_minAddr <= ia && ia <= mid_maxAddr); - return mid_secMap; - } - // NOTREACHED - } - - private: - // sorted array of per-object ranges, non overlapping, non empty - std::vector<SecMap*> mSecMaps; - - // a logging sink, for debugging. - void (*mLog)(const char*); -}; - - -//////////////////////////////////////////////////////////////// -// LUL // -//////////////////////////////////////////////////////////////// - -#define LUL_LOG(_str) \ - do { \ - char buf[200]; \ - SprintfLiteral(buf, \ - "LUL: pid %d tid %d lul-obj %p: %s", \ - getpid(), gettid(), this, (_str)); \ - buf[sizeof(buf)-1] = 0; \ - mLog(buf); \ - } while (0) - -LUL::LUL(void (*aLog)(const char*)) - : mLog(aLog) - , mAdminMode(true) - , mAdminThreadId(gettid()) - , mPriMap(new PriMap(aLog)) - , mSegArray(new SegArray()) - , mUSU(new UniqueStringUniverse()) -{ - LUL_LOG("LUL::LUL: Created object"); -} - - -LUL::~LUL() -{ - LUL_LOG("LUL::~LUL: Destroyed object"); - delete mPriMap; - delete mSegArray; - mLog = nullptr; - delete mUSU; -} - - -void -LUL::MaybeShowStats() -{ - // This is racey in the sense that it can't guarantee that - // n_new == n_new_Context + n_new_CFI + n_new_Scanned - // if it should happen that mStats is updated by some other thread - // in between computation of n_new and n_new_{Context,CFI,Scanned}. - // But it's just stats printing, so we don't really care. - uint32_t n_new = mStats - mStatsPrevious; - if (n_new >= 5000) { - uint32_t n_new_Context = mStats.mContext - mStatsPrevious.mContext; - uint32_t n_new_CFI = mStats.mCFI - mStatsPrevious.mCFI; - uint32_t n_new_Scanned = mStats.mScanned - mStatsPrevious.mScanned; - mStatsPrevious = mStats; - char buf[200]; - SprintfLiteral(buf, - "LUL frame stats: TOTAL %5u" - " CTX %4u CFI %4u SCAN %4u", - n_new, n_new_Context, n_new_CFI, n_new_Scanned); - buf[sizeof(buf)-1] = 0; - mLog(buf); - } -} - - -void -LUL::EnableUnwinding() -{ - LUL_LOG("LUL::EnableUnwinding"); - // Don't assert for Admin mode here. That is, tolerate a call here - // if we are already in Unwinding mode. - MOZ_ASSERT(gettid() == mAdminThreadId); - - mAdminMode = false; -} - - -void -LUL::NotifyAfterMap(uintptr_t aRXavma, size_t aSize, - const char* aFileName, const void* aMappedImage) -{ - MOZ_ASSERT(mAdminMode); - MOZ_ASSERT(gettid() == mAdminThreadId); - - mLog(":\n"); - char buf[200]; - SprintfLiteral(buf, "NotifyMap %llx %llu %s\n", - (unsigned long long int)aRXavma, (unsigned long long int)aSize, - aFileName); - buf[sizeof(buf)-1] = 0; - mLog(buf); - - // Ignore obviously-stupid notifications. - if (aSize > 0) { - - // Here's a new mapping, for this object. - SecMap* smap = new SecMap(mLog); - - // Read CFI or EXIDX unwind data into |smap|. - if (!aMappedImage) { - (void)lul::ReadSymbolData( - string(aFileName), std::vector<string>(), smap, - (void*)aRXavma, aSize, mUSU, mLog); - } else { - (void)lul::ReadSymbolDataInternal( - (const uint8_t*)aMappedImage, - string(aFileName), std::vector<string>(), smap, - (void*)aRXavma, aSize, mUSU, mLog); - } - - mLog("NotifyMap .. preparing entries\n"); - - smap->PrepareRuleSets(aRXavma, aSize); - - SprintfLiteral(buf, - "NotifyMap got %lld entries\n", (long long int)smap->Size()); - buf[sizeof(buf)-1] = 0; - mLog(buf); - - // Add it to the primary map (the top level set of mapped objects). - mPriMap->AddSecMap(smap); - - // Tell the segment array about the mapping, so that the stack - // scan and __kernel_syscall mechanisms know where valid code is. - mSegArray->add(aRXavma, aRXavma + aSize - 1, true); - } -} - - -void -LUL::NotifyExecutableArea(uintptr_t aRXavma, size_t aSize) -{ - MOZ_ASSERT(mAdminMode); - MOZ_ASSERT(gettid() == mAdminThreadId); - - mLog(":\n"); - char buf[200]; - SprintfLiteral(buf, "NotifyExecutableArea %llx %llu\n", - (unsigned long long int)aRXavma, (unsigned long long int)aSize); - buf[sizeof(buf)-1] = 0; - mLog(buf); - - // Ignore obviously-stupid notifications. - if (aSize > 0) { - // Tell the segment array about the mapping, so that the stack - // scan and __kernel_syscall mechanisms know where valid code is. - mSegArray->add(aRXavma, aRXavma + aSize - 1, true); - } -} - - -void -LUL::NotifyBeforeUnmap(uintptr_t aRXavmaMin, uintptr_t aRXavmaMax) -{ - MOZ_ASSERT(mAdminMode); - MOZ_ASSERT(gettid() == mAdminThreadId); - - mLog(":\n"); - char buf[100]; - SprintfLiteral(buf, "NotifyUnmap %016llx-%016llx\n", - (unsigned long long int)aRXavmaMin, - (unsigned long long int)aRXavmaMax); - buf[sizeof(buf)-1] = 0; - mLog(buf); - - MOZ_ASSERT(aRXavmaMin <= aRXavmaMax); - - // Remove from the primary map, any secondary maps that intersect - // with the address range. Also delete the secondary maps. - mPriMap->RemoveSecMapsInRange(aRXavmaMin, aRXavmaMax); - - // Tell the segment array that the address range no longer - // contains valid code. - mSegArray->add(aRXavmaMin, aRXavmaMax, false); - - SprintfLiteral(buf, "NotifyUnmap: now have %d SecMaps\n", - (int)mPriMap->CountSecMaps()); - buf[sizeof(buf)-1] = 0; - mLog(buf); -} - - -size_t -LUL::CountMappings() -{ - MOZ_ASSERT(mAdminMode); - MOZ_ASSERT(gettid() == mAdminThreadId); - - return mPriMap->CountSecMaps(); -} - - -// RUNS IN NO-MALLOC CONTEXT -static -TaggedUWord DerefTUW(TaggedUWord aAddr, const StackImage* aStackImg) -{ - if (!aAddr.Valid()) { - return TaggedUWord(); - } - - // Lower limit check. |aAddr.Value()| is the lowest requested address - // and |aStackImg->mStartAvma| is the lowest address we actually have, - // so the comparison is straightforward. - if (aAddr.Value() < aStackImg->mStartAvma) { - return TaggedUWord(); - } - - // Upper limit check. We must compute the highest requested address - // and the highest address we actually have, but being careful to - // avoid overflow. In particular if |aAddr| is 0xFFF...FFF or the - // 3/7 values below that, then we will get overflow. See bug #1245477. - typedef CheckedInt<uintptr_t> CheckedUWord; - CheckedUWord highest_requested_plus_one - = CheckedUWord(aAddr.Value()) + CheckedUWord(sizeof(uintptr_t)); - CheckedUWord highest_available_plus_one - = CheckedUWord(aStackImg->mStartAvma) + CheckedUWord(aStackImg->mLen); - if (!highest_requested_plus_one.isValid() // overflow? - || !highest_available_plus_one.isValid() // overflow? - || (highest_requested_plus_one.value() - > highest_available_plus_one.value())) { // in range? - return TaggedUWord(); - } - - return TaggedUWord(*(uintptr_t*)(aStackImg->mContents + aAddr.Value() - - aStackImg->mStartAvma)); -} - -// RUNS IN NO-MALLOC CONTEXT -static -TaggedUWord EvaluateReg(int16_t aReg, const UnwindRegs* aOldRegs, - TaggedUWord aCFA) -{ - switch (aReg) { - case DW_REG_CFA: return aCFA; -#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - case DW_REG_INTEL_XBP: return aOldRegs->xbp; - case DW_REG_INTEL_XSP: return aOldRegs->xsp; - case DW_REG_INTEL_XIP: return aOldRegs->xip; -#elif defined(LUL_ARCH_arm) - case DW_REG_ARM_R7: return aOldRegs->r7; - case DW_REG_ARM_R11: return aOldRegs->r11; - case DW_REG_ARM_R12: return aOldRegs->r12; - case DW_REG_ARM_R13: return aOldRegs->r13; - case DW_REG_ARM_R14: return aOldRegs->r14; - case DW_REG_ARM_R15: return aOldRegs->r15; -#else -# error "Unsupported arch" -#endif - default: MOZ_ASSERT(0); return TaggedUWord(); - } -} - -// RUNS IN NO-MALLOC CONTEXT -// See prototype for comment. -TaggedUWord EvaluatePfxExpr(int32_t start, - const UnwindRegs* aOldRegs, - TaggedUWord aCFA, const StackImage* aStackImg, - const vector<PfxInstr>& aPfxInstrs) -{ - // A small evaluation stack, and a stack pointer, which points to - // the highest numbered in-use element. - const int N_STACK = 10; - TaggedUWord stack[N_STACK]; - int stackPointer = -1; - for (int i = 0; i < N_STACK; i++) - stack[i] = TaggedUWord(); - -# define PUSH(_tuw) \ - do { \ - if (stackPointer >= N_STACK-1) goto fail; /* overflow */ \ - stack[++stackPointer] = (_tuw); \ - } while (0) - -# define POP(_lval) \ - do { \ - if (stackPointer < 0) goto fail; /* underflow */ \ - _lval = stack[stackPointer--]; \ - } while (0) - - // Cursor in the instruction sequence. - size_t curr = start + 1; - - // Check the start point is sane. - size_t nInstrs = aPfxInstrs.size(); - if (start < 0 || (size_t)start >= nInstrs) - goto fail; - - { - // The instruction sequence must start with PX_Start. If not, - // something is seriously wrong. - PfxInstr first = aPfxInstrs[start]; - if (first.mOpcode != PX_Start) - goto fail; - - // Push the CFA on the stack to start with (or not), as required by - // the original DW_OP_*expression* CFI. - if (first.mOperand != 0) - PUSH(aCFA); - } - - while (true) { - if (curr >= nInstrs) - goto fail; // ran off the end of the sequence - - PfxInstr pfxi = aPfxInstrs[curr++]; - if (pfxi.mOpcode == PX_End) - break; // we're done - - switch (pfxi.mOpcode) { - case PX_Start: - // This should appear only at the start of the sequence. - goto fail; - case PX_End: - // We just took care of that, so we shouldn't see it again. - MOZ_ASSERT(0); - goto fail; - case PX_SImm32: - PUSH(TaggedUWord((intptr_t)pfxi.mOperand)); - break; - case PX_DwReg: { - DW_REG_NUMBER reg = (DW_REG_NUMBER)pfxi.mOperand; - MOZ_ASSERT(reg != DW_REG_CFA); - PUSH(EvaluateReg(reg, aOldRegs, aCFA)); - break; - } - case PX_Deref: { - TaggedUWord addr; - POP(addr); - PUSH(DerefTUW(addr, aStackImg)); - break; - } - case PX_Add: { - TaggedUWord x, y; - POP(x); POP(y); PUSH(y + x); - break; - } - case PX_Sub: { - TaggedUWord x, y; - POP(x); POP(y); PUSH(y - x); - break; - } - case PX_And: { - TaggedUWord x, y; - POP(x); POP(y); PUSH(y & x); - break; - } - case PX_Or: { - TaggedUWord x, y; - POP(x); POP(y); PUSH(y | x); - break; - } - case PX_CmpGES: { - TaggedUWord x, y; - POP(x); POP(y); PUSH(y.CmpGEs(x)); - break; - } - case PX_Shl: { - TaggedUWord x, y; - POP(x); POP(y); PUSH(y << x); - break; - } - default: - MOZ_ASSERT(0); - goto fail; - } - } // while (true) - - // Evaluation finished. The top value on the stack is the result. - if (stackPointer >= 0) { - return stack[stackPointer]; - } - // Else fall through - - fail: - return TaggedUWord(); - -# undef PUSH -# undef POP -} - -// RUNS IN NO-MALLOC CONTEXT -TaggedUWord LExpr::EvaluateExpr(const UnwindRegs* aOldRegs, - TaggedUWord aCFA, const StackImage* aStackImg, - const vector<PfxInstr>* aPfxInstrs) const -{ - switch (mHow) { - case UNKNOWN: - return TaggedUWord(); - case NODEREF: { - TaggedUWord tuw = EvaluateReg(mReg, aOldRegs, aCFA); - tuw = tuw + TaggedUWord((intptr_t)mOffset); - return tuw; - } - case DEREF: { - TaggedUWord tuw = EvaluateReg(mReg, aOldRegs, aCFA); - tuw = tuw + TaggedUWord((intptr_t)mOffset); - return DerefTUW(tuw, aStackImg); - } - case PFXEXPR: { - MOZ_ASSERT(aPfxInstrs); - if (!aPfxInstrs) { - return TaggedUWord(); - } - return EvaluatePfxExpr(mOffset, aOldRegs, aCFA, aStackImg, *aPfxInstrs); - } - default: - MOZ_ASSERT(0); - return TaggedUWord(); - } -} - -// RUNS IN NO-MALLOC CONTEXT -static -void UseRuleSet(/*MOD*/UnwindRegs* aRegs, - const StackImage* aStackImg, const RuleSet* aRS, - const vector<PfxInstr>* aPfxInstrs) -{ - // Take a copy of regs, since we'll need to refer to the old values - // whilst computing the new ones. - UnwindRegs old_regs = *aRegs; - - // Mark all the current register values as invalid, so that the - // caller can see, on our return, which ones have been computed - // anew. If we don't even manage to compute a new PC value, then - // the caller will have to abandon the unwind. - // FIXME: Create and use instead: aRegs->SetAllInvalid(); -#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - aRegs->xbp = TaggedUWord(); - aRegs->xsp = TaggedUWord(); - aRegs->xip = TaggedUWord(); -#elif defined(LUL_ARCH_arm) - aRegs->r7 = TaggedUWord(); - aRegs->r11 = TaggedUWord(); - aRegs->r12 = TaggedUWord(); - aRegs->r13 = TaggedUWord(); - aRegs->r14 = TaggedUWord(); - aRegs->r15 = TaggedUWord(); -#else -# error "Unsupported arch" -#endif - - // This is generally useful. - const TaggedUWord inval = TaggedUWord(); - - // First, compute the CFA. - TaggedUWord cfa - = aRS->mCfaExpr.EvaluateExpr(&old_regs, - inval/*old cfa*/, aStackImg, aPfxInstrs); - - // If we didn't manage to compute the CFA, well .. that's ungood, - // but keep going anyway. It'll be OK provided none of the register - // value rules mention the CFA. In any case, compute the new values - // for each register that we're tracking. - -#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - aRegs->xbp - = aRS->mXbpExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs); - aRegs->xsp - = aRS->mXspExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs); - aRegs->xip - = aRS->mXipExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs); -#elif defined(LUL_ARCH_arm) - aRegs->r7 - = aRS->mR7expr .EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs); - aRegs->r11 - = aRS->mR11expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs); - aRegs->r12 - = aRS->mR12expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs); - aRegs->r13 - = aRS->mR13expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs); - aRegs->r14 - = aRS->mR14expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs); - aRegs->r15 - = aRS->mR15expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs); -#else -# error "Unsupported arch" -#endif - - // We're done. Any regs for which we didn't manage to compute a - // new value will now be marked as invalid. -} - -// RUNS IN NO-MALLOC CONTEXT -void -LUL::Unwind(/*OUT*/uintptr_t* aFramePCs, - /*OUT*/uintptr_t* aFrameSPs, - /*OUT*/size_t* aFramesUsed, - /*OUT*/size_t* aScannedFramesAcquired, - size_t aFramesAvail, - size_t aScannedFramesAllowed, - UnwindRegs* aStartRegs, StackImage* aStackImg) -{ - MOZ_ASSERT(!mAdminMode); - - ///////////////////////////////////////////////////////// - // BEGIN UNWIND - - *aFramesUsed = 0; - - UnwindRegs regs = *aStartRegs; - TaggedUWord last_valid_sp = TaggedUWord(); - - // Stack-scan control - unsigned int n_scanned_frames = 0; // # s-s frames recovered so far - static const int NUM_SCANNED_WORDS = 50; // max allowed scan length - - while (true) { - - if (DEBUG_MAIN) { - char buf[300]; - mLog("\n"); -#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - SprintfLiteral(buf, - "LoopTop: rip %d/%llx rsp %d/%llx rbp %d/%llx\n", - (int)regs.xip.Valid(), (unsigned long long int)regs.xip.Value(), - (int)regs.xsp.Valid(), (unsigned long long int)regs.xsp.Value(), - (int)regs.xbp.Valid(), (unsigned long long int)regs.xbp.Value()); - buf[sizeof(buf)-1] = 0; - mLog(buf); -#elif defined(LUL_ARCH_arm) - SprintfLiteral(buf, - "LoopTop: r15 %d/%llx r7 %d/%llx r11 %d/%llx" - " r12 %d/%llx r13 %d/%llx r14 %d/%llx\n", - (int)regs.r15.Valid(), (unsigned long long int)regs.r15.Value(), - (int)regs.r7.Valid(), (unsigned long long int)regs.r7.Value(), - (int)regs.r11.Valid(), (unsigned long long int)regs.r11.Value(), - (int)regs.r12.Valid(), (unsigned long long int)regs.r12.Value(), - (int)regs.r13.Valid(), (unsigned long long int)regs.r13.Value(), - (int)regs.r14.Valid(), (unsigned long long int)regs.r14.Value()); - buf[sizeof(buf)-1] = 0; - mLog(buf); -#else -# error "Unsupported arch" -#endif - } - -#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - TaggedUWord ia = regs.xip; - TaggedUWord sp = regs.xsp; -#elif defined(LUL_ARCH_arm) - TaggedUWord ia = (*aFramesUsed == 0 ? regs.r15 : regs.r14); - TaggedUWord sp = regs.r13; -#else -# error "Unsupported arch" -#endif - - if (*aFramesUsed >= aFramesAvail) { - break; - } - - // If we don't have a valid value for the PC, give up. - if (!ia.Valid()) { - break; - } - - // If this is the innermost frame, record the SP value, which - // presumably is valid. If this isn't the innermost frame, and we - // have a valid SP value, check that its SP value isn't less that - // the one we've seen so far, so as to catch potential SP value - // cycles. - if (*aFramesUsed == 0) { - last_valid_sp = sp; - } else { - MOZ_ASSERT(last_valid_sp.Valid()); - if (sp.Valid()) { - if (sp.Value() < last_valid_sp.Value()) { - // Hmm, SP going in the wrong direction. Let's stop. - break; - } - // Remember where we got to. - last_valid_sp = sp; - } - } - - // For the innermost frame, the IA value is what we need. For all - // other frames, it's actually the return address, so back up one - // byte so as to get it into the calling instruction. - aFramePCs[*aFramesUsed] = ia.Value() - (*aFramesUsed == 0 ? 0 : 1); - aFrameSPs[*aFramesUsed] = sp.Valid() ? sp.Value() : 0; - (*aFramesUsed)++; - - // Find the RuleSet for the current IA, if any. This will also - // query the backing (secondary) maps if it isn't found in the - // thread-local cache. - - // If this isn't the innermost frame, back up into the calling insn. - if (*aFramesUsed > 1) { - ia = ia + TaggedUWord((uintptr_t)(-1)); - } - - pair<const RuleSet*, const vector<PfxInstr>*> ruleset_and_pfxinstrs - = mPriMap->Lookup(ia.Value()); - const RuleSet* ruleset = ruleset_and_pfxinstrs.first; - const vector<PfxInstr>* pfxinstrs = ruleset_and_pfxinstrs.second; - - if (DEBUG_MAIN) { - char buf[100]; - SprintfLiteral(buf, "ruleset for 0x%llx = %p\n", - (unsigned long long int)ia.Value(), ruleset); - buf[sizeof(buf)-1] = 0; - mLog(buf); - } - - ///////////////////////////////////////////// - //// - // On 32 bit x86-linux, syscalls are often done via the VDSO - // function __kernel_vsyscall, which doesn't have a corresponding - // object that we can read debuginfo from. That effectively kills - // off all stack traces for threads blocked in syscalls. Hence - // special-case by looking at the code surrounding the program - // counter. - // - // 0xf7757420 <__kernel_vsyscall+0>: push %ecx - // 0xf7757421 <__kernel_vsyscall+1>: push %edx - // 0xf7757422 <__kernel_vsyscall+2>: push %ebp - // 0xf7757423 <__kernel_vsyscall+3>: mov %esp,%ebp - // 0xf7757425 <__kernel_vsyscall+5>: sysenter - // 0xf7757427 <__kernel_vsyscall+7>: nop - // 0xf7757428 <__kernel_vsyscall+8>: nop - // 0xf7757429 <__kernel_vsyscall+9>: nop - // 0xf775742a <__kernel_vsyscall+10>: nop - // 0xf775742b <__kernel_vsyscall+11>: nop - // 0xf775742c <__kernel_vsyscall+12>: nop - // 0xf775742d <__kernel_vsyscall+13>: nop - // 0xf775742e <__kernel_vsyscall+14>: int $0x80 - // 0xf7757430 <__kernel_vsyscall+16>: pop %ebp - // 0xf7757431 <__kernel_vsyscall+17>: pop %edx - // 0xf7757432 <__kernel_vsyscall+18>: pop %ecx - // 0xf7757433 <__kernel_vsyscall+19>: ret - // - // In cases where the sampled thread is blocked in a syscall, its - // program counter will point at "pop %ebp". Hence we look for - // the sequence "int $0x80; pop %ebp; pop %edx; pop %ecx; ret", and - // the corresponding register-recovery actions are: - // new_ebp = *(old_esp + 0) - // new eip = *(old_esp + 12) - // new_esp = old_esp + 16 - // - // It may also be the case that the program counter points two - // nops before the "int $0x80", viz, is __kernel_vsyscall+12, in - // the case where the syscall has been restarted but the thread - // hasn't been rescheduled. The code below doesn't handle that; - // it could easily be made to. - // -#if defined(LUL_PLAT_x86_android) || defined(LUL_PLAT_x86_linux) - if (!ruleset && *aFramesUsed == 1 && ia.Valid() && sp.Valid()) { - uintptr_t insns_min, insns_max; - uintptr_t eip = ia.Value(); - bool b = mSegArray->getBoundingCodeSegment(&insns_min, &insns_max, eip); - if (b && eip - 2 >= insns_min && eip + 3 <= insns_max) { - uint8_t* eipC = (uint8_t*)eip; - if (eipC[-2] == 0xCD && eipC[-1] == 0x80 && eipC[0] == 0x5D && - eipC[1] == 0x5A && eipC[2] == 0x59 && eipC[3] == 0xC3) { - TaggedUWord sp_plus_0 = sp; - TaggedUWord sp_plus_12 = sp; - TaggedUWord sp_plus_16 = sp; - sp_plus_12 = sp_plus_12 + TaggedUWord(12); - sp_plus_16 = sp_plus_16 + TaggedUWord(16); - TaggedUWord new_ebp = DerefTUW(sp_plus_0, aStackImg); - TaggedUWord new_eip = DerefTUW(sp_plus_12, aStackImg); - TaggedUWord new_esp = sp_plus_16; - if (new_ebp.Valid() && new_eip.Valid() && new_esp.Valid()) { - regs.xbp = new_ebp; - regs.xip = new_eip; - regs.xsp = new_esp; - continue; - } - } - } - } -#endif - //// - ///////////////////////////////////////////// - - // So, do we have a ruleset for this address? If so, use it now. - if (ruleset) { - - if (DEBUG_MAIN) { - ruleset->Print(mLog); mLog("\n"); - } - // Use the RuleSet to compute the registers for the previous - // frame. |regs| is modified in-place. - UseRuleSet(®s, aStackImg, ruleset, pfxinstrs); - - } else { - - // There's no RuleSet for the specified address, so see if - // it's possible to get anywhere by stack-scanning. - - // Use stack scanning frugally. - if (n_scanned_frames++ >= aScannedFramesAllowed) { - break; - } - - // We can't scan the stack without a valid, aligned stack pointer. - if (!sp.IsAligned()) { - break; - } - - bool scan_succeeded = false; - for (int i = 0; i < NUM_SCANNED_WORDS; ++i) { - TaggedUWord aWord = DerefTUW(sp, aStackImg); - // aWord is something we fished off the stack. It should be - // valid, unless we overran the stack bounds. - if (!aWord.Valid()) { - break; - } - - // Now, does aWord point inside a text section and immediately - // after something that looks like a call instruction? - if (mPriMap->MaybeIsReturnPoint(aWord, mSegArray)) { - // Yes it does. Update the unwound registers heuristically, - // using the same schemes as Breakpad does. - scan_succeeded = true; - (*aScannedFramesAcquired)++; - -#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - // The same logic applies for the 32- and 64-bit cases. - // Register names of the form xsp etc refer to (eg) esp in - // the 32-bit case and rsp in the 64-bit case. -# if defined(LUL_ARCH_x64) - const int wordSize = 8; -# else - const int wordSize = 4; -# endif - // The return address -- at XSP -- will have been pushed by - // the CALL instruction. So the caller's XSP value - // immediately before and after that CALL instruction is the - // word above XSP. - regs.xsp = sp + TaggedUWord(wordSize); - - // aWord points at the return point, so back up one byte - // to put it in the calling instruction. - regs.xip = aWord + TaggedUWord((uintptr_t)(-1)); - - // Computing a new value from the frame pointer is more tricky. - if (regs.xbp.Valid() && - sp.Valid() && regs.xbp.Value() == sp.Value() - wordSize) { - // One possibility is that the callee begins with the standard - // preamble "push %xbp; mov %xsp, %xbp". In which case, the - // (1) caller's XBP value will be at the word below XSP, and - // (2) the current (callee's) XBP will point at that word: - regs.xbp = DerefTUW(regs.xbp, aStackImg); - } else if (regs.xbp.Valid() && - sp.Valid() && regs.xbp.Value() >= sp.Value() + wordSize) { - // If that didn't work out, maybe the callee didn't change - // XBP, so it still holds the caller's value. For that to - // be plausible, XBP will need to have a value at least - // higher than XSP since that holds the purported return - // address. In which case do nothing, since XBP already - // holds the "right" value. - } else { - // Mark XBP as invalid, so that subsequent unwind iterations - // don't assume it holds valid data. - regs.xbp = TaggedUWord(); - } - - // Move on to the next word up the stack - sp = sp + TaggedUWord(wordSize); - -#elif defined(LUL_ARCH_arm) - // Set all registers to be undefined, except for SP(R13) and - // PC(R15). - - // aWord points either at the return point, if returning to - // ARM code, or one insn past the return point if returning - // to Thumb code. In both cases, aWord-2 is guaranteed to - // fall within the calling instruction. - regs.r15 = aWord + TaggedUWord((uintptr_t)(-2)); - - // Make SP be the word above the location where the return - // address was found. - regs.r13 = sp + TaggedUWord(4); - - // All other regs are undefined. - regs.r7 = regs.r11 = regs.r12 = regs.r14 = TaggedUWord(); - - // Move on to the next word up the stack - sp = sp + TaggedUWord(4); - -#else -# error "Unknown plat" -#endif - - break; - } - - } // for (int i = 0; i < NUM_SCANNED_WORDS; i++) - - // We tried to make progress by scanning the stack, but failed. - // So give up -- fall out of the top level unwind loop. - if (!scan_succeeded) { - break; - } - } - - } // top level unwind loop - - // END UNWIND - ///////////////////////////////////////////////////////// -} - - -//////////////////////////////////////////////////////////////// -// LUL Unit Testing // -//////////////////////////////////////////////////////////////// - -static const int LUL_UNIT_TEST_STACK_SIZE = 16384; - -// This function is innermost in the test call sequence. It uses LUL -// to unwind, and compares the result with the sequence specified in -// the director string. These need to agree in order for the test to -// pass. In order not to screw up the results, this function needs -// to have a not-very big stack frame, since we're only presenting -// the innermost LUL_UNIT_TEST_STACK_SIZE bytes of stack to LUL, and -// that chunk unavoidably includes the frame for this function. -// -// This function must not be inlined into its callers. Doing so will -// cause the expected-vs-actual backtrace consistency checking to -// fail. Prints summary results to |aLUL|'s logging sink and also -// returns a boolean indicating whether or not the test passed. -static __attribute__((noinline)) -bool GetAndCheckStackTrace(LUL* aLUL, const char* dstring) -{ - // Get hold of the current unwind-start registers. - UnwindRegs startRegs; - memset(&startRegs, 0, sizeof(startRegs)); -#if defined(LUL_PLAT_x64_linux) - volatile uintptr_t block[3]; - MOZ_ASSERT(sizeof(block) == 24); - __asm__ __volatile__( - "leaq 0(%%rip), %%r15" "\n\t" - "movq %%r15, 0(%0)" "\n\t" - "movq %%rsp, 8(%0)" "\n\t" - "movq %%rbp, 16(%0)" "\n" - : : "r"(&block[0]) : "memory", "r15" - ); - startRegs.xip = TaggedUWord(block[0]); - startRegs.xsp = TaggedUWord(block[1]); - startRegs.xbp = TaggedUWord(block[2]); - const uintptr_t REDZONE_SIZE = 128; - uintptr_t start = block[1] - REDZONE_SIZE; -#elif defined(LUL_PLAT_x86_linux) || defined(LUL_PLAT_x86_android) - volatile uintptr_t block[3]; - MOZ_ASSERT(sizeof(block) == 12); - __asm__ __volatile__( - ".byte 0xE8,0x00,0x00,0x00,0x00"/*call next insn*/ "\n\t" - "popl %%edi" "\n\t" - "movl %%edi, 0(%0)" "\n\t" - "movl %%esp, 4(%0)" "\n\t" - "movl %%ebp, 8(%0)" "\n" - : : "r"(&block[0]) : "memory", "edi" - ); - startRegs.xip = TaggedUWord(block[0]); - startRegs.xsp = TaggedUWord(block[1]); - startRegs.xbp = TaggedUWord(block[2]); - const uintptr_t REDZONE_SIZE = 0; - uintptr_t start = block[1] - REDZONE_SIZE; -#elif defined(LUL_PLAT_arm_android) - volatile uintptr_t block[6]; - MOZ_ASSERT(sizeof(block) == 24); - __asm__ __volatile__( - "mov r0, r15" "\n\t" - "str r0, [%0, #0]" "\n\t" - "str r14, [%0, #4]" "\n\t" - "str r13, [%0, #8]" "\n\t" - "str r12, [%0, #12]" "\n\t" - "str r11, [%0, #16]" "\n\t" - "str r7, [%0, #20]" "\n" - : : "r"(&block[0]) : "memory", "r0" - ); - startRegs.r15 = TaggedUWord(block[0]); - startRegs.r14 = TaggedUWord(block[1]); - startRegs.r13 = TaggedUWord(block[2]); - startRegs.r12 = TaggedUWord(block[3]); - startRegs.r11 = TaggedUWord(block[4]); - startRegs.r7 = TaggedUWord(block[5]); - const uintptr_t REDZONE_SIZE = 0; - uintptr_t start = block[1] - REDZONE_SIZE; -#else -# error "Unsupported platform" -#endif - - // Get hold of the innermost LUL_UNIT_TEST_STACK_SIZE bytes of the - // stack. - uintptr_t end = start + LUL_UNIT_TEST_STACK_SIZE; - uintptr_t ws = sizeof(void*); - start &= ~(ws-1); - end &= ~(ws-1); - uintptr_t nToCopy = end - start; - if (nToCopy > lul::N_STACK_BYTES) { - nToCopy = lul::N_STACK_BYTES; - } - MOZ_ASSERT(nToCopy <= lul::N_STACK_BYTES); - StackImage* stackImg = new StackImage(); - stackImg->mLen = nToCopy; - stackImg->mStartAvma = start; - if (nToCopy > 0) { - MOZ_MAKE_MEM_DEFINED((void*)start, nToCopy); - memcpy(&stackImg->mContents[0], (void*)start, nToCopy); - } - - // Unwind it. - const int MAX_TEST_FRAMES = 64; - uintptr_t framePCs[MAX_TEST_FRAMES]; - uintptr_t frameSPs[MAX_TEST_FRAMES]; - size_t framesAvail = mozilla::ArrayLength(framePCs); - size_t framesUsed = 0; - size_t scannedFramesAllowed = 0; - size_t scannedFramesAcquired = 0; - aLUL->Unwind( &framePCs[0], &frameSPs[0], - &framesUsed, &scannedFramesAcquired, - framesAvail, scannedFramesAllowed, - &startRegs, stackImg ); - - delete stackImg; - - //if (0) { - // // Show what we have. - // fprintf(stderr, "Got %d frames:\n", (int)framesUsed); - // for (size_t i = 0; i < framesUsed; i++) { - // fprintf(stderr, " [%2d] SP %p PC %p\n", - // (int)i, (void*)frameSPs[i], (void*)framePCs[i]); - // } - // fprintf(stderr, "\n"); - //} - - // Check to see if there's a consistent binding between digits in - // the director string ('1' .. '8') and the PC values acquired by - // the unwind. If there isn't, the unwinding has failed somehow. - uintptr_t binding[8]; // binding for '1' .. binding for '8' - memset((void*)binding, 0, sizeof(binding)); - - // The general plan is to work backwards along the director string - // and forwards along the framePCs array. Doing so corresponds to - // working outwards from the innermost frame of the recursive test set. - const char* cursor = dstring; - - // Find the end. This leaves |cursor| two bytes past the first - // character we want to look at -- see comment below. - while (*cursor) cursor++; - - // Counts the number of consistent frames. - size_t nConsistent = 0; - - // Iterate back to the start of the director string. The starting - // points are a bit complex. We can't use framePCs[0] because that - // contains the PC in this frame (above). We can't use framePCs[1] - // because that will contain the PC at return point in the recursive - // test group (TestFn[1-8]) for their call "out" to this function, - // GetAndCheckStackTrace. Although LUL will compute a correct - // return address, that will not be the same return address as for a - // recursive call out of the the function to another function in the - // group. Hence we can only start consistency checking at - // framePCs[2]. - // - // To be consistent, then, we must ignore the last element in the - // director string as that corresponds to framePCs[1]. Hence the - // start points are: framePCs[2] and the director string 2 bytes - // before the terminating zero. - // - // Also as a result of this, the number of consistent frames counted - // will always be one less than the length of the director string - // (not including its terminating zero). - size_t frameIx; - for (cursor = cursor-2, frameIx = 2; - cursor >= dstring && frameIx < framesUsed; - cursor--, frameIx++) { - char c = *cursor; - uintptr_t pc = framePCs[frameIx]; - // If this doesn't hold, the director string is ill-formed. - MOZ_ASSERT(c >= '1' && c <= '8'); - int n = ((int)c) - ((int)'1'); - if (binding[n] == 0) { - // There's no binding for |c| yet, so install |pc| and carry on. - binding[n] = pc; - nConsistent++; - continue; - } - // There's a pre-existing binding for |c|. Check it's consistent. - if (binding[n] != pc) { - // Not consistent. Give up now. - break; - } - // Consistent. Keep going. - nConsistent++; - } - - // So, did we succeed? - bool passed = nConsistent+1 == strlen(dstring); - - // Show the results. - char buf[200]; - SprintfLiteral(buf, "LULUnitTest: dstring = %s\n", dstring); - buf[sizeof(buf)-1] = 0; - aLUL->mLog(buf); - SprintfLiteral(buf, - "LULUnitTest: %d consistent, %d in dstring: %s\n", - (int)nConsistent, (int)strlen(dstring), - passed ? "PASS" : "FAIL"); - buf[sizeof(buf)-1] = 0; - aLUL->mLog(buf); - - return passed; -} - - -// Macro magic to create a set of 8 mutually recursive functions with -// varying frame sizes. These will recurse amongst themselves as -// specified by |strP|, the directory string, and call -// GetAndCheckStackTrace when the string becomes empty, passing it the -// original value of the string. This checks the result, printing -// results on |aLUL|'s logging sink, and also returns a boolean -// indicating whether or not the results are acceptable (correct). - -#define DECL_TEST_FN(NAME) \ - bool NAME(LUL* aLUL, const char* strPorig, const char* strP); - -#define GEN_TEST_FN(NAME, FRAMESIZE) \ - bool NAME(LUL* aLUL, const char* strPorig, const char* strP) { \ - volatile char space[FRAMESIZE]; \ - memset((char*)&space[0], 0, sizeof(space)); \ - if (*strP == '\0') { \ - /* We've come to the end of the director string. */ \ - /* Take a stack snapshot. */ \ - return GetAndCheckStackTrace(aLUL, strPorig); \ - } else { \ - /* Recurse onwards. This is a bit subtle. The obvious */ \ - /* thing to do here is call onwards directly, from within the */ \ - /* arms of the case statement. That gives a problem in that */ \ - /* there will be multiple return points inside each function when */ \ - /* unwinding, so it will be difficult to check for consistency */ \ - /* against the director string. Instead, we make an indirect */ \ - /* call, so as to guarantee that there is only one call site */ \ - /* within each function. This does assume that the compiler */ \ - /* won't transform it back to the simple direct-call form. */ \ - /* To discourage it from doing so, the call is bracketed with */ \ - /* __asm__ __volatile__ sections so as to make it not-movable. */ \ - bool (*nextFn)(LUL*, const char*, const char*) = NULL; \ - switch (*strP) { \ - case '1': nextFn = TestFn1; break; \ - case '2': nextFn = TestFn2; break; \ - case '3': nextFn = TestFn3; break; \ - case '4': nextFn = TestFn4; break; \ - case '5': nextFn = TestFn5; break; \ - case '6': nextFn = TestFn6; break; \ - case '7': nextFn = TestFn7; break; \ - case '8': nextFn = TestFn8; break; \ - default: nextFn = TestFn8; break; \ - } \ - __asm__ __volatile__("":::"cc","memory"); \ - bool passed = nextFn(aLUL, strPorig, strP+1); \ - __asm__ __volatile__("":::"cc","memory"); \ - return passed; \ - } \ - } - -// The test functions are mutually recursive, so it is necessary to -// declare them before defining them. -DECL_TEST_FN(TestFn1) -DECL_TEST_FN(TestFn2) -DECL_TEST_FN(TestFn3) -DECL_TEST_FN(TestFn4) -DECL_TEST_FN(TestFn5) -DECL_TEST_FN(TestFn6) -DECL_TEST_FN(TestFn7) -DECL_TEST_FN(TestFn8) - -GEN_TEST_FN(TestFn1, 123) -GEN_TEST_FN(TestFn2, 456) -GEN_TEST_FN(TestFn3, 789) -GEN_TEST_FN(TestFn4, 23) -GEN_TEST_FN(TestFn5, 47) -GEN_TEST_FN(TestFn6, 117) -GEN_TEST_FN(TestFn7, 1) -GEN_TEST_FN(TestFn8, 99) - - -// This starts the test sequence going. Call here to generate a -// sequence of calls as directed by the string |dstring|. The call -// sequence will, from its innermost frame, finish by calling -// GetAndCheckStackTrace() and passing it |dstring|. -// GetAndCheckStackTrace() will unwind the stack, check consistency -// of those results against |dstring|, and print a pass/fail message -// to aLUL's logging sink. It also updates the counters in *aNTests -// and aNTestsPassed. -__attribute__((noinline)) void -TestUnw(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed, - LUL* aLUL, const char* dstring) -{ - // Ensure that the stack has at least this much space on it. This - // makes it safe to saw off the top LUL_UNIT_TEST_STACK_SIZE bytes - // and hand it to LUL. Safe in the sense that no segfault can - // happen because the stack is at least this big. This is all - // somewhat dubious in the sense that a sufficiently clever compiler - // (clang, for one) can figure out that space[] is unused and delete - // it from the frame. Hence the somewhat elaborate hoop jumping to - // fill it up before the call and to at least appear to use the - // value afterwards. - int i; - volatile char space[LUL_UNIT_TEST_STACK_SIZE]; - for (i = 0; i < LUL_UNIT_TEST_STACK_SIZE; i++) { - space[i] = (char)(i & 0x7F); - } - - // Really run the test. - bool passed = TestFn1(aLUL, dstring, dstring); - - // Appear to use space[], by visiting the value to compute some kind - // of checksum, and then (apparently) using the checksum. - int sum = 0; - for (i = 0; i < LUL_UNIT_TEST_STACK_SIZE; i++) { - // If this doesn't fool LLVM, I don't know what will. - sum += space[i] - 3*i; - } - __asm__ __volatile__("" : : "r"(sum)); - - // Update the counters. - (*aNTests)++; - if (passed) { - (*aNTestsPassed)++; - } -} - - -void -RunLulUnitTests(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed, LUL* aLUL) -{ - aLUL->mLog(":\n"); - aLUL->mLog("LULUnitTest: BEGIN\n"); - *aNTests = *aNTestsPassed = 0; - TestUnw(aNTests, aNTestsPassed, aLUL, "11111111"); - TestUnw(aNTests, aNTestsPassed, aLUL, "11222211"); - TestUnw(aNTests, aNTestsPassed, aLUL, "111222333"); - TestUnw(aNTests, aNTestsPassed, aLUL, "1212121231212331212121212121212"); - TestUnw(aNTests, aNTestsPassed, aLUL, "31415827271828325332173258"); - TestUnw(aNTests, aNTestsPassed, aLUL, - "123456781122334455667788777777777777777777777"); - aLUL->mLog("LULUnitTest: END\n"); - aLUL->mLog(":\n"); -} - - -} // namespace lul diff --git a/tools/profiler/lul/LulMain.h b/tools/profiler/lul/LulMain.h deleted file mode 100644 index 0916d1b26..000000000 --- a/tools/profiler/lul/LulMain.h +++ /dev/null @@ -1,397 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef LulMain_h -#define LulMain_h - -#include "LulPlatformMacros.h" -#include "mozilla/Atomics.h" - -// LUL: A Lightweight Unwind Library. -// This file provides the end-user (external) interface for LUL. - -// Some comments about naming in the implementation. These are safe -// to ignore if you are merely using LUL, but are important if you -// hack on its internals. -// -// Debuginfo readers in general have tended to use the word "address" -// to mean several different things. This sometimes makes them -// difficult to understand and maintain. LUL tries hard to avoid -// using the word "address" and instead uses the following more -// precise terms: -// -// * SVMA ("Stated Virtual Memory Address"): this is an address of a -// symbol (etc) as it is stated in the symbol table, or other -// metadata, of an object. Such values are typically small and -// start from zero or thereabouts, unless the object has been -// prelinked. -// -// * AVMA ("Actual Virtual Memory Address"): this is the address of a -// symbol (etc) in a running process, that is, once the associated -// object has been mapped into a process. Such values are typically -// much larger than SVMAs, since objects can get mapped arbitrarily -// far along the address space. -// -// * "Bias": the difference between AVMA and SVMA for a given symbol -// (specifically, AVMA - SVMA). The bias is always an integral -// number of pages. Once we know the bias for a given object's -// text section (for example), we can compute the AVMAs of all of -// its text symbols by adding the bias to their SVMAs. -// -// * "Image address": typically, to read debuginfo from an object we -// will temporarily mmap in the file so as to read symbol tables -// etc. Addresses in this temporary mapping are called "Image -// addresses". Note that the temporary mapping is entirely -// unrelated to the mappings of the file that the dynamic linker -// must perform merely in order to get the program to run. Hence -// image addresses are unrelated to either SVMAs or AVMAs. - - -namespace lul { - -// A machine word plus validity tag. -class TaggedUWord { -public: - // RUNS IN NO-MALLOC CONTEXT - // Construct a valid one. - explicit TaggedUWord(uintptr_t w) - : mValue(w) - , mValid(true) - {} - - // RUNS IN NO-MALLOC CONTEXT - // Construct an invalid one. - TaggedUWord() - : mValue(0) - , mValid(false) - {} - - // RUNS IN NO-MALLOC CONTEXT - TaggedUWord operator+(TaggedUWord rhs) const { - return (Valid() && rhs.Valid()) ? TaggedUWord(Value() + rhs.Value()) - : TaggedUWord(); - } - - // RUNS IN NO-MALLOC CONTEXT - TaggedUWord operator-(TaggedUWord rhs) const { - return (Valid() && rhs.Valid()) ? TaggedUWord(Value() - rhs.Value()) - : TaggedUWord(); - } - - // RUNS IN NO-MALLOC CONTEXT - TaggedUWord operator&(TaggedUWord rhs) const { - return (Valid() && rhs.Valid()) ? TaggedUWord(Value() & rhs.Value()) - : TaggedUWord(); - } - - // RUNS IN NO-MALLOC CONTEXT - TaggedUWord operator|(TaggedUWord rhs) const { - return (Valid() && rhs.Valid()) ? TaggedUWord(Value() | rhs.Value()) - : TaggedUWord(); - } - - // RUNS IN NO-MALLOC CONTEXT - TaggedUWord CmpGEs(TaggedUWord rhs) const { - if (Valid() && rhs.Valid()) { - intptr_t s1 = (intptr_t)Value(); - intptr_t s2 = (intptr_t)rhs.Value(); - return TaggedUWord(s1 >= s2 ? 1 : 0); - } - return TaggedUWord(); - } - - // RUNS IN NO-MALLOC CONTEXT - TaggedUWord operator<<(TaggedUWord rhs) const { - if (Valid() && rhs.Valid()) { - uintptr_t shift = rhs.Value(); - if (shift < 8 * sizeof(uintptr_t)) - return TaggedUWord(Value() << shift); - } - return TaggedUWord(); - } - - // RUNS IN NO-MALLOC CONTEXT - // Is equal? Note: non-validity on either side gives non-equality. - bool operator==(TaggedUWord other) const { - return (mValid && other.Valid()) ? (mValue == other.Value()) : false; - } - - // RUNS IN NO-MALLOC CONTEXT - // Is it word-aligned? - bool IsAligned() const { - return mValid && (mValue & (sizeof(uintptr_t)-1)) == 0; - } - - // RUNS IN NO-MALLOC CONTEXT - uintptr_t Value() const { return mValue; } - - // RUNS IN NO-MALLOC CONTEXT - bool Valid() const { return mValid; } - -private: - uintptr_t mValue; - bool mValid; -}; - - -// The registers, with validity tags, that will be unwound. - -struct UnwindRegs { -#if defined(LUL_ARCH_arm) - TaggedUWord r7; - TaggedUWord r11; - TaggedUWord r12; - TaggedUWord r13; - TaggedUWord r14; - TaggedUWord r15; -#elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86) - TaggedUWord xbp; - TaggedUWord xsp; - TaggedUWord xip; -#else -# error "Unknown plat" -#endif -}; - - -// The maximum number of bytes in a stack snapshot. This can be -// increased if necessary, but larger values cost performance, since a -// stack snapshot needs to be copied between sampling and worker -// threads for each snapshot. In practice 32k seems to be enough -// to get good backtraces. -static const size_t N_STACK_BYTES = 32768; - -// The stack chunk image that will be unwound. -struct StackImage { - // [start_avma, +len) specify the address range in the buffer. - // Obviously we require 0 <= len <= N_STACK_BYTES. - uintptr_t mStartAvma; - size_t mLen; - uint8_t mContents[N_STACK_BYTES]; -}; - - -// Statistics collection for the unwinder. -template<typename T> -class LULStats { -public: - LULStats() - : mContext(0) - , mCFI(0) - , mScanned(0) - {} - - template <typename S> - explicit LULStats(const LULStats<S>& aOther) - : mContext(aOther.mContext) - , mCFI(aOther.mCFI) - , mScanned(aOther.mScanned) - {} - - template <typename S> - LULStats<T>& operator=(const LULStats<S>& aOther) - { - mContext = aOther.mContext; - mCFI = aOther.mCFI; - mScanned = aOther.mScanned; - return *this; - } - - template <typename S> - uint32_t operator-(const LULStats<S>& aOther) { - return (mContext - aOther.mContext) + - (mCFI - aOther.mCFI) + (mScanned - aOther.mScanned); - } - - T mContext; // Number of context frames - T mCFI; // Number of CFI/EXIDX frames - T mScanned; // Number of scanned frames -}; - - -// The core unwinder library class. Just one of these is needed, and -// it can be shared by multiple unwinder threads. -// -// The library operates in one of two modes. -// -// * Admin mode. The library is this state after creation. In Admin -// mode, no unwinding may be performed. It is however allowable to -// perform administrative tasks -- primarily, loading of unwind info -// -- in this mode. In particular, it is safe for the library to -// perform dynamic memory allocation in this mode. Safe in the -// sense that there is no risk of deadlock against unwinding threads -// that might -- because of where they have been sampled -- hold the -// system's malloc lock. -// -// * Unwind mode. In this mode, calls to ::Unwind may be made, but -// nothing else. ::Unwind guarantees not to make any dynamic memory -// requests, so as to guarantee that the calling thread won't -// deadlock in the case where it already holds the system's malloc lock. -// -// The library is created in Admin mode. After debuginfo is loaded, -// the caller must switch it into Unwind mode by calling -// ::EnableUnwinding. There is no way to switch it back to Admin mode -// after that. To safely switch back to Admin mode would require the -// caller (or other external agent) to guarantee that there are no -// pending ::Unwind calls. - -class PriMap; -class SegArray; -class UniqueStringUniverse; - -class LUL { -public: - // Create; supply a logging sink. Sets the object in Admin mode. - explicit LUL(void (*aLog)(const char*)); - - // Destroy. Caller is responsible for ensuring that no other - // threads are in Unwind calls. All resources are freed and all - // registered unwinder threads are deregistered. Can be called - // either in Admin or Unwind mode. - ~LUL(); - - // Notify the library that unwinding is now allowed and so - // admin-mode calls are no longer allowed. The object is initially - // created in admin mode. The only possible transition is - // admin->unwinding, therefore. - void EnableUnwinding(); - - // Notify of a new r-x mapping, and load the associated unwind info. - // The filename is strdup'd and used for debug printing. If - // aMappedImage is NULL, this function will mmap/munmap the file - // itself, so as to be able to read the unwind info. If - // aMappedImage is non-NULL then it is assumed to point to a - // called-supplied and caller-managed mapped image of the file. - // May only be called in Admin mode. - void NotifyAfterMap(uintptr_t aRXavma, size_t aSize, - const char* aFileName, const void* aMappedImage); - - // In rare cases we know an executable area exists but don't know - // what the associated file is. This call notifies LUL of such - // areas. This is important for correct functioning of stack - // scanning and of the x86-{linux,android} special-case - // __kernel_syscall function handling. - // This must be called only after the code area in - // question really has been mapped. - // May only be called in Admin mode. - void NotifyExecutableArea(uintptr_t aRXavma, size_t aSize); - - // Notify that a mapped area has been unmapped; discard any - // associated unwind info. Acquires mRWlock for writing. Note that - // to avoid segfaulting the stack-scan unwinder, which inspects code - // areas, this must be called before the code area in question is - // really unmapped. Note that, unlike NotifyAfterMap(), this - // function takes the start and end addresses of the range to be - // unmapped, rather than a start and a length parameter. This is so - // as to make it possible to notify an unmap for the entire address - // space using a single call. - // May only be called in Admin mode. - void NotifyBeforeUnmap(uintptr_t aAvmaMin, uintptr_t aAvmaMax); - - // Apply NotifyBeforeUnmap to the entire address space. This causes - // LUL to discard all unwind and executable-area information for the - // entire address space. - // May only be called in Admin mode. - void NotifyBeforeUnmapAll() { - NotifyBeforeUnmap(0, UINTPTR_MAX); - } - - // Returns the number of mappings currently registered. - // May only be called in Admin mode. - size_t CountMappings(); - - // Unwind |aStackImg| starting with the context in |aStartRegs|. - // Write the number of frames recovered in *aFramesUsed. Put - // the PC values in aFramePCs[0 .. *aFramesUsed-1] and - // the SP values in aFrameSPs[0 .. *aFramesUsed-1]. - // |aFramesAvail| is the size of the two output arrays and hence the - // largest possible value of *aFramesUsed. PC values are always - // valid, and the unwind will stop when the PC becomes invalid, but - // the SP values might be invalid, in which case the value zero will - // be written in the relevant frameSPs[] slot. - // - // Unwinding may optionally use stack scanning. The maximum number - // of frames that may be recovered by stack scanning is - // |aScannedFramesAllowed| and the actual number recovered is - // written into *aScannedFramesAcquired. |aScannedFramesAllowed| - // must be less than or equal to |aFramesAvail|. - // - // This function assumes that the SP values increase as it unwinds - // away from the innermost frame -- that is, that the stack grows - // down. It monitors SP values as it unwinds to check they - // decrease, so as to avoid looping on corrupted stacks. - // - // May only be called in Unwind mode. Multiple threads may unwind - // at once. LUL user is responsible for ensuring that no thread makes - // any Admin calls whilst in Unwind mode. - // MOZ_CRASHes if the calling thread is not registered for unwinding. - // - // Up to aScannedFramesAllowed stack-scanned frames may be recovered. - // - // The calling thread must previously have been registered via a call to - // RegisterSampledThread. - void Unwind(/*OUT*/uintptr_t* aFramePCs, - /*OUT*/uintptr_t* aFrameSPs, - /*OUT*/size_t* aFramesUsed, - /*OUT*/size_t* aScannedFramesAcquired, - size_t aFramesAvail, - size_t aScannedFramesAllowed, - UnwindRegs* aStartRegs, StackImage* aStackImg); - - // The logging sink. Call to send debug strings to the caller- - // specified destination. Can only be called by the Admin thread. - void (*mLog)(const char*); - - // Statistics relating to unwinding. These have to be atomic since - // unwinding can occur on different threads simultaneously. - LULStats<mozilla::Atomic<uint32_t>> mStats; - - // Possibly show the statistics. This may not be called from any - // registered sampling thread, since it involves I/O. - void MaybeShowStats(); - -private: - // The statistics counters at the point where they were last printed. - LULStats<uint32_t> mStatsPrevious; - - // Are we in admin mode? Initially |true| but changes to |false| - // once unwinding begins. - bool mAdminMode; - - // The thread ID associated with admin mode. This is the only thread - // that is allowed do perform non-Unwind calls on this object. Conversely, - // no registered Unwinding thread may be the admin thread. This is so - // as to clearly partition the one thread that may do dynamic memory - // allocation from the threads that are being sampled, since the latter - // absolutely may not do dynamic memory allocation. - int mAdminThreadId; - - // The top level mapping from code address ranges to postprocessed - // unwind info. Basically a sorted array of (addr, len, info) - // records. This field is updated by NotifyAfterMap and NotifyBeforeUnmap. - PriMap* mPriMap; - - // An auxiliary structure that records which address ranges are - // mapped r-x, for the benefit of the stack scanner. - SegArray* mSegArray; - - // A UniqueStringUniverse that holds all the strdup'd strings created - // whilst reading unwind information. This is included so as to make - // it possible to free them in ~LUL. - UniqueStringUniverse* mUSU; -}; - - -// Run unit tests on an initialised, loaded-up LUL instance, and print -// summary results on |aLUL|'s logging sink. Also return the number -// of tests run in *aNTests and the number that passed in -// *aNTestsPassed. -void -RunLulUnitTests(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed, LUL* aLUL); - -} // namespace lul - -#endif // LulMain_h diff --git a/tools/profiler/lul/platform-linux-lul.cpp b/tools/profiler/lul/platform-linux-lul.cpp deleted file mode 100644 index da764f3cf..000000000 --- a/tools/profiler/lul/platform-linux-lul.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include <stdio.h> -#include <signal.h> -#include <string.h> -#include <stdlib.h> -#include <time.h> - -#include "platform.h" -#include "PlatformMacros.h" -#include "LulMain.h" -#include "shared-libraries.h" -#include "AutoObjectMapper.h" - -// Contains miscellaneous helpers that are used to connect SPS and LUL. - - -// Find out, in a platform-dependent way, where the code modules got -// mapped in the process' virtual address space, and get |aLUL| to -// load unwind info for them. -void -read_procmaps(lul::LUL* aLUL) -{ - MOZ_ASSERT(aLUL->CountMappings() == 0); - -# if defined(SPS_OS_linux) || defined(SPS_OS_android) || defined(SPS_OS_darwin) - SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf(); - - for (size_t i = 0; i < info.GetSize(); i++) { - const SharedLibrary& lib = info.GetEntry(i); - -# if defined(SPS_OS_android) - // We're using faulty.lib. Use a special-case object mapper. - AutoObjectMapperFaultyLib mapper(aLUL->mLog); -# else - // We can use the standard POSIX-based mapper. - AutoObjectMapperPOSIX mapper(aLUL->mLog); -# endif - - // Ask |mapper| to map the object. Then hand its mapped address - // to NotifyAfterMap(). - void* image = nullptr; - size_t size = 0; - bool ok = mapper.Map(&image, &size, lib.GetName()); - if (ok && image && size > 0) { - aLUL->NotifyAfterMap(lib.GetStart(), lib.GetEnd()-lib.GetStart(), - lib.GetName().c_str(), image); - } else if (!ok && lib.GetName() == "") { - // The object has no name and (as a consequence) the mapper - // failed to map it. This happens on Linux, where - // GetInfoForSelf() produces two such mappings: one for the - // executable and one for the VDSO. The executable one isn't a - // big deal since there's not much interesting code in there, - // but the VDSO one is a problem on x86-{linux,android} because - // lack of knowledge about the mapped area inhibits LUL's - // special __kernel_syscall handling. Hence notify |aLUL| at - // least of the mapping, even though it can't read any unwind - // information for the area. - aLUL->NotifyExecutableArea(lib.GetStart(), lib.GetEnd()-lib.GetStart()); - } - - // |mapper| goes out of scope at this point and so its destructor - // unmaps the object. - } - -# else -# error "Unknown platform" -# endif -} - - -// LUL needs a callback for its logging sink. -void -logging_sink_for_LUL(const char* str) { - // Ignore any trailing \n, since LOG will add one anyway. - size_t n = strlen(str); - if (n > 0 && str[n-1] == '\n') { - char* tmp = strdup(str); - tmp[n-1] = 0; - LOG(tmp); - free(tmp); - } else { - LOG(str); - } -} diff --git a/tools/profiler/lul/platform-linux-lul.h b/tools/profiler/lul/platform-linux-lul.h deleted file mode 100644 index 4698cd388..000000000 --- a/tools/profiler/lul/platform-linux-lul.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef MOZ_PLATFORM_LINUX_LUL_H -#define MOZ_PLATFORM_LINUX_LUL_H - -#include "platform.h" - -// Find out, in a platform-dependent way, where the code modules got -// mapped in the process' virtual address space, and get |aLUL| to -// load unwind info for them. -void -read_procmaps(lul::LUL* aLUL); - -// LUL needs a callback for its logging sink. -void -logging_sink_for_LUL(const char* str); - -// A singleton instance of the library. -extern lul::LUL* sLUL; - -#endif /* ndef MOZ_PLATFORM_LINUX_LUL_H */ |