mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Bug 938157 - Lightweight CFI/EXIDX unwinding library for SPS. r=n.nethercote, nfroyd, bgirard.
--HG-- extra : rebase_source : bd427749667ddd6641eff414879c3706a5cb5f5e
This commit is contained in:
parent
e8b418c5f5
commit
9bd60ea2d4
207
tools/profiler/AutoObjectMapper.cpp
Normal file
207
tools/profiler/AutoObjectMapper.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
/* -*- 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/NullPtr.h"
|
||||
|
||||
#include "PlatformMacros.h"
|
||||
#include "AutoObjectMapper.h"
|
||||
|
||||
#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
|
||||
# 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];
|
||||
snprintf(buf, sizeof(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) && !defined(MOZ_WIDGET_GONK)
|
||||
// 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) && !defined(MOZ_WIDGET_GONK)
|
115
tools/profiler/AutoObjectMapper.h
Normal file
115
tools/profiler/AutoObjectMapper.h
Normal file
@ -0,0 +1,115 @@
|
||||
/* -*- 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|.
|
||||
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) && !defined(MOZ_WIDGET_GONK)
|
||||
// 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) && !defined(MOZ_WIDGET_GONK)
|
||||
|
||||
#endif // AutoObjectMapper_h
|
110
tools/profiler/LulCommon.cpp
Normal file
110
tools/profiler/LulCommon.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/* -*- 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:
|
||||
UniqueString(string str) { str_ = strdup(str.c_str()); }
|
||||
~UniqueString() { free(reinterpret_cast<void*>(const_cast<char*>(str_))); }
|
||||
const char* str_;
|
||||
};
|
||||
|
||||
class UniqueStringUniverse {
|
||||
public:
|
||||
UniqueStringUniverse() {};
|
||||
const UniqueString* FindOrCopy(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;
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::map<string, UniqueString*> map_;
|
||||
};
|
||||
|
||||
|
||||
const UniqueString* ToUniqueString(string str) {
|
||||
// For the initialisation of sUniverse to be threadsafe,
|
||||
// this relies on C++11's semantics.
|
||||
static UniqueStringUniverse sUniverse;
|
||||
return sUniverse.FindOrCopy(str);
|
||||
}
|
||||
|
||||
const char* const FromUniqueString(const UniqueString* ustr)
|
||||
{
|
||||
return ustr->str_;
|
||||
}
|
||||
|
||||
} // namespace lul
|
582
tools/profiler/LulCommonExt.h
Normal file
582
tools/profiler/LulCommonExt.h
Normal file
@ -0,0 +1,582 @@
|
||||
/* -*- 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, 2010, 2012, 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>
|
||||
|
||||
// module.h: Define google_breakpad::Module. A Module holds debugging
|
||||
// information, and can write that information out as a Breakpad
|
||||
// symbol file.
|
||||
|
||||
|
||||
// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
|
||||
// Copyright (c) 2001, 2002 Peter Dimov
|
||||
//
|
||||
// Permission to copy, use, modify, sell and distribute this software
|
||||
// is granted provided this copyright notice appears in all copies.
|
||||
// This software is provided "as is" without express or implied
|
||||
// warranty, and with no claim as to its suitability for any purpose.
|
||||
//
|
||||
// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation.
|
||||
//
|
||||
|
||||
|
||||
// This file is derived from the following files in
|
||||
// toolkit/crashreporter/google-breakpad:
|
||||
// src/common/unique_string.h
|
||||
// src/common/scoped_ptr.h
|
||||
// src/common/module.h
|
||||
|
||||
// External interface for the "Common" component of LUL.
|
||||
|
||||
#ifndef LulCommonExt_h
|
||||
#define LulCommonExt_h
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <cstddef> // for std::ptrdiff_t
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
|
||||
namespace lul {
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// UniqueString
|
||||
//
|
||||
|
||||
// Abstract type
|
||||
class UniqueString;
|
||||
|
||||
// Unique-ify a string. |ToUniqueString| can never return nullptr.
|
||||
const UniqueString* ToUniqueString(std::string);
|
||||
|
||||
// Get the contained C string (debugging only)
|
||||
const char* const FromUniqueString(const UniqueString*);
|
||||
|
||||
// Some handy pre-uniqified strings. Z is an escape character:
|
||||
// ZS '$'
|
||||
// ZD '.'
|
||||
// Zeq '='
|
||||
// Zplus '+'
|
||||
// Zstar '*'
|
||||
// Zslash '/'
|
||||
// Zpercent '%'
|
||||
// Zat '@'
|
||||
// Zcaret '^'
|
||||
|
||||
// Note that ustr__empty and (UniqueString*)nullptr are considered
|
||||
// to be different.
|
||||
//
|
||||
// Unfortunately these have to be written as functions so as to
|
||||
// make them safe to use in static initialisers.
|
||||
|
||||
// ""
|
||||
inline static const UniqueString* ustr__empty() {
|
||||
static const UniqueString* us = nullptr;
|
||||
if (!us) us = ToUniqueString("");
|
||||
return us;
|
||||
}
|
||||
|
||||
// ".cfa"
|
||||
inline static const UniqueString* ustr__ZDcfa() {
|
||||
static const UniqueString* us = nullptr;
|
||||
if (!us) us = ToUniqueString(".cfa");
|
||||
return us;
|
||||
}
|
||||
|
||||
// ".ra"
|
||||
inline static const UniqueString* ustr__ZDra() {
|
||||
static const UniqueString* us = nullptr;
|
||||
if (!us) us = ToUniqueString(".ra");
|
||||
return us;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// GUID
|
||||
//
|
||||
|
||||
typedef struct {
|
||||
uint32_t data1;
|
||||
uint16_t data2;
|
||||
uint16_t data3;
|
||||
uint8_t data4[8];
|
||||
} MDGUID; // GUID
|
||||
|
||||
typedef MDGUID GUID;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// scoped_ptr
|
||||
//
|
||||
|
||||
// scoped_ptr mimics a built-in pointer except that it guarantees deletion
|
||||
// of the object pointed to, either on destruction of the scoped_ptr or via
|
||||
// an explicit reset(). scoped_ptr is a simple solution for simple needs;
|
||||
// use shared_ptr or std::auto_ptr if your needs are more complex.
|
||||
|
||||
// *** NOTE ***
|
||||
// If your scoped_ptr is a class member of class FOO pointing to a
|
||||
// forward declared type BAR (as shown below), then you MUST use a non-inlined
|
||||
// version of the destructor. The destructor of a scoped_ptr (called from
|
||||
// FOO's destructor) must have a complete definition of BAR in order to
|
||||
// destroy it. Example:
|
||||
//
|
||||
// -- foo.h --
|
||||
// class BAR;
|
||||
//
|
||||
// class FOO {
|
||||
// public:
|
||||
// FOO();
|
||||
// ~FOO(); // Required for sources that instantiate class FOO to compile!
|
||||
//
|
||||
// private:
|
||||
// scoped_ptr<BAR> bar_;
|
||||
// };
|
||||
//
|
||||
// -- foo.cc --
|
||||
// #include "foo.h"
|
||||
// FOO::~FOO() {} // Empty, but must be non-inlined to FOO's class definition.
|
||||
|
||||
// scoped_ptr_malloc added by Google
|
||||
// When one of these goes out of scope, instead of doing a delete or
|
||||
// delete[], it calls free(). scoped_ptr_malloc<char> is likely to see
|
||||
// much more use than any other specializations.
|
||||
|
||||
// release() added by Google
|
||||
// Use this to conditionally transfer ownership of a heap-allocated object
|
||||
// to the caller, usually on method success.
|
||||
|
||||
template <typename T>
|
||||
class scoped_ptr {
|
||||
private:
|
||||
|
||||
T* ptr;
|
||||
|
||||
scoped_ptr(scoped_ptr const &);
|
||||
scoped_ptr & operator=(scoped_ptr const &);
|
||||
|
||||
public:
|
||||
|
||||
typedef T element_type;
|
||||
|
||||
explicit scoped_ptr(T* p = 0): ptr(p) {}
|
||||
|
||||
~scoped_ptr() {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
void reset(T* p = 0) {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
|
||||
if (ptr != p) {
|
||||
delete ptr;
|
||||
ptr = p;
|
||||
}
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
MOZ_ASSERT(ptr != 0);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
MOZ_ASSERT(ptr != 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool operator==(T* p) const {
|
||||
return ptr == p;
|
||||
}
|
||||
|
||||
bool operator!=(T* p) const {
|
||||
return ptr != p;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void swap(scoped_ptr & b) {
|
||||
T* tmp = b.ptr;
|
||||
b.ptr = ptr;
|
||||
ptr = tmp;
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* tmp = ptr;
|
||||
ptr = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// no reason to use these: each scoped_ptr should have its own object
|
||||
template <typename U> bool operator==(scoped_ptr<U> const& p) const;
|
||||
template <typename U> bool operator!=(scoped_ptr<U> const& p) const;
|
||||
};
|
||||
|
||||
template<typename T> inline
|
||||
void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<typename T> inline
|
||||
bool operator==(T* p, const scoped_ptr<T>& b) {
|
||||
return p == b.get();
|
||||
}
|
||||
|
||||
template<typename T> inline
|
||||
bool operator!=(T* p, const scoped_ptr<T>& b) {
|
||||
return p != b.get();
|
||||
}
|
||||
|
||||
// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
|
||||
// is guaranteed, either on destruction of the scoped_array or via an explicit
|
||||
// reset(). Use shared_array or std::vector if your needs are more complex.
|
||||
|
||||
template<typename T>
|
||||
class scoped_array {
|
||||
private:
|
||||
|
||||
T* ptr;
|
||||
|
||||
scoped_array(scoped_array const &);
|
||||
scoped_array & operator=(scoped_array const &);
|
||||
|
||||
public:
|
||||
|
||||
typedef T element_type;
|
||||
|
||||
explicit scoped_array(T* p = 0) : ptr(p) {}
|
||||
|
||||
~scoped_array() {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
delete[] ptr;
|
||||
}
|
||||
|
||||
void reset(T* p = 0) {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
|
||||
if (ptr != p) {
|
||||
delete [] ptr;
|
||||
ptr = p;
|
||||
}
|
||||
}
|
||||
|
||||
T& operator[](std::ptrdiff_t i) const {
|
||||
MOZ_ASSERT(ptr != 0);
|
||||
MOZ_ASSERT(i >= 0);
|
||||
return ptr[i];
|
||||
}
|
||||
|
||||
bool operator==(T* p) const {
|
||||
return ptr == p;
|
||||
}
|
||||
|
||||
bool operator!=(T* p) const {
|
||||
return ptr != p;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void swap(scoped_array & b) {
|
||||
T* tmp = b.ptr;
|
||||
b.ptr = ptr;
|
||||
ptr = tmp;
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* tmp = ptr;
|
||||
ptr = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// no reason to use these: each scoped_array should have its own object
|
||||
template <typename U> bool operator==(scoped_array<U> const& p) const;
|
||||
template <typename U> bool operator!=(scoped_array<U> const& p) const;
|
||||
};
|
||||
|
||||
template<class T> inline
|
||||
void swap(scoped_array<T>& a, scoped_array<T>& b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<typename T> inline
|
||||
bool operator==(T* p, const scoped_array<T>& b) {
|
||||
return p == b.get();
|
||||
}
|
||||
|
||||
template<typename T> inline
|
||||
bool operator!=(T* p, const scoped_array<T>& b) {
|
||||
return p != b.get();
|
||||
}
|
||||
|
||||
|
||||
// This class wraps the c library function free() in a class that can be
|
||||
// passed as a template argument to scoped_ptr_malloc below.
|
||||
class ScopedPtrMallocFree {
|
||||
public:
|
||||
inline void operator()(void* x) const {
|
||||
free(x);
|
||||
}
|
||||
};
|
||||
|
||||
// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
|
||||
// second template argument, the functor used to free the object.
|
||||
|
||||
template<typename T, typename FreeProc = ScopedPtrMallocFree>
|
||||
class scoped_ptr_malloc {
|
||||
private:
|
||||
|
||||
T* ptr;
|
||||
|
||||
scoped_ptr_malloc(scoped_ptr_malloc const &);
|
||||
scoped_ptr_malloc & operator=(scoped_ptr_malloc const &);
|
||||
|
||||
public:
|
||||
|
||||
typedef T element_type;
|
||||
|
||||
explicit scoped_ptr_malloc(T* p = 0): ptr(p) {}
|
||||
|
||||
~scoped_ptr_malloc() {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
free_((void*) ptr);
|
||||
}
|
||||
|
||||
void reset(T* p = 0) {
|
||||
typedef char type_must_be_complete[sizeof(T)];
|
||||
|
||||
if (ptr != p) {
|
||||
free_((void*) ptr);
|
||||
ptr = p;
|
||||
}
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
MOZ_ASSERT(ptr != 0);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
MOZ_ASSERT(ptr != 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool operator==(T* p) const {
|
||||
return ptr == p;
|
||||
}
|
||||
|
||||
bool operator!=(T* p) const {
|
||||
return ptr != p;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void swap(scoped_ptr_malloc & b) {
|
||||
T* tmp = b.ptr;
|
||||
b.ptr = ptr;
|
||||
ptr = tmp;
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* tmp = ptr;
|
||||
ptr = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// no reason to use these: each scoped_ptr_malloc should have its own object
|
||||
template <typename U, typename GP>
|
||||
bool operator==(scoped_ptr_malloc<U, GP> const& p) const;
|
||||
template <typename U, typename GP>
|
||||
bool operator!=(scoped_ptr_malloc<U, GP> const& p) const;
|
||||
|
||||
static FreeProc const free_;
|
||||
};
|
||||
|
||||
template<typename T, typename FP>
|
||||
FP const scoped_ptr_malloc<T,FP>::free_ = FP();
|
||||
|
||||
template<typename T, typename FP> inline
|
||||
void swap(scoped_ptr_malloc<T,FP>& a, scoped_ptr_malloc<T,FP>& b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<typename T, typename FP> inline
|
||||
bool operator==(T* p, const scoped_ptr_malloc<T,FP>& b) {
|
||||
return p == b.get();
|
||||
}
|
||||
|
||||
template<typename T, typename FP> inline
|
||||
bool operator!=(T* p, const scoped_ptr_malloc<T,FP>& b) {
|
||||
return p != b.get();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Module
|
||||
//
|
||||
|
||||
// A Module represents the contents of a module, and supports methods
|
||||
// for adding information produced by parsing STABS or DWARF data
|
||||
// --- possibly both from the same file --- and then writing out the
|
||||
// unified contents as a Breakpad-format symbol file.
|
||||
class Module {
|
||||
public:
|
||||
// The type of addresses and sizes in a symbol table.
|
||||
typedef uint64_t Address;
|
||||
|
||||
// Representation of an expression. This can either be a postfix
|
||||
// expression, in which case it is stored as a string, or a simple
|
||||
// expression of the form (identifier + imm) or *(identifier + imm).
|
||||
// It can also be invalid (denoting "no value").
|
||||
enum ExprHow {
|
||||
kExprInvalid = 1,
|
||||
kExprPostfix,
|
||||
kExprSimple,
|
||||
kExprSimpleMem
|
||||
};
|
||||
|
||||
struct Expr {
|
||||
// Construct a simple-form expression
|
||||
Expr(const UniqueString* ident, long offset, bool deref) {
|
||||
if (ident == ustr__empty()) {
|
||||
Expr();
|
||||
} else {
|
||||
postfix_ = "";
|
||||
ident_ = ident;
|
||||
offset_ = offset;
|
||||
how_ = deref ? kExprSimpleMem : kExprSimple;
|
||||
}
|
||||
}
|
||||
|
||||
// Construct an invalid expression
|
||||
Expr() {
|
||||
postfix_ = "";
|
||||
ident_ = nullptr;
|
||||
offset_ = 0;
|
||||
how_ = kExprInvalid;
|
||||
}
|
||||
|
||||
// Return the postfix expression string, either directly,
|
||||
// if this is a postfix expression, or by synthesising it
|
||||
// for a simple expression.
|
||||
std::string getExprPostfix() const {
|
||||
switch (how_) {
|
||||
case kExprPostfix:
|
||||
return postfix_;
|
||||
case kExprSimple:
|
||||
case kExprSimpleMem: {
|
||||
char buf[40];
|
||||
sprintf(buf, " %ld %c%s", labs(offset_), offset_ < 0 ? '-' : '+',
|
||||
how_ == kExprSimple ? "" : " ^");
|
||||
return std::string(FromUniqueString(ident_)) + std::string(buf);
|
||||
}
|
||||
case kExprInvalid:
|
||||
default:
|
||||
MOZ_ASSERT(0 && "getExprPostfix: invalid Module::Expr type");
|
||||
return "Expr::genExprPostfix: kExprInvalid";
|
||||
}
|
||||
}
|
||||
|
||||
// The identifier that gives the starting value for simple expressions.
|
||||
const UniqueString* ident_;
|
||||
// The offset to add for simple expressions.
|
||||
long offset_;
|
||||
// The Postfix expression string to evaluate for non-simple expressions.
|
||||
std::string postfix_;
|
||||
// The operation expressed by this expression.
|
||||
ExprHow how_;
|
||||
};
|
||||
|
||||
// A map from register names to expressions that recover
|
||||
// their values. This can represent a complete set of rules to
|
||||
// follow at some address, or a set of changes to be applied to an
|
||||
// extant set of rules.
|
||||
// NOTE! there are two completely different types called RuleMap. This
|
||||
// is one of them.
|
||||
typedef std::map<const UniqueString*, Expr> RuleMap;
|
||||
|
||||
// A map from addresses to RuleMaps, representing changes that take
|
||||
// effect at given addresses.
|
||||
typedef std::map<Address, RuleMap> RuleChangeMap;
|
||||
|
||||
// A range of 'STACK CFI' stack walking information. An instance of
|
||||
// this structure corresponds to a 'STACK CFI INIT' record and the
|
||||
// subsequent 'STACK CFI' records that fall within its range.
|
||||
struct StackFrameEntry {
|
||||
// The starting address and number of bytes of machine code this
|
||||
// entry covers.
|
||||
Address address, size;
|
||||
|
||||
// The initial register recovery rules, in force at the starting
|
||||
// address.
|
||||
RuleMap initial_rules;
|
||||
|
||||
// A map from addresses to rule changes. To find the rules in
|
||||
// force at a given address, start with initial_rules, and then
|
||||
// apply the changes given in this map for all addresses up to and
|
||||
// including the address you're interested in.
|
||||
RuleChangeMap rule_changes;
|
||||
};
|
||||
|
||||
// Create a new module with the given name, operating system,
|
||||
// architecture, and ID string.
|
||||
Module(const std::string &name, const std::string &os,
|
||||
const std::string &architecture, const std::string &id);
|
||||
~Module();
|
||||
|
||||
private:
|
||||
|
||||
// Module header entries.
|
||||
std::string name_, os_, architecture_, id_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace lul
|
||||
|
||||
#endif // LulCommonExt_h
|
2010
tools/profiler/LulDwarf.cpp
Normal file
2010
tools/profiler/LulDwarf.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1272
tools/profiler/LulDwarfExt.h
Normal file
1272
tools/profiler/LulDwarfExt.h
Normal file
File diff suppressed because it is too large
Load Diff
127
tools/profiler/LulDwarfInt.h
Normal file
127
tools/profiler/LulDwarfInt.h
Normal file
@ -0,0 +1,127 @@
|
||||
/* -*- 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) 2008, 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>
|
||||
|
||||
// This file is derived from the following file in
|
||||
// toolkit/crashreporter/google-breakpad:
|
||||
// src/common/dwarf/dwarf2enums.h
|
||||
|
||||
#ifndef LulDwarfInt_h
|
||||
#define LulDwarfInt_h
|
||||
|
||||
#include "LulCommonExt.h"
|
||||
#include "LulDwarfExt.h"
|
||||
|
||||
namespace lul {
|
||||
|
||||
// These enums do not follow the google3 style only because they are
|
||||
// known universally (specs, other implementations) by the names in
|
||||
// exactly this capitalization.
|
||||
// Tag names and codes.
|
||||
|
||||
// Call Frame Info instructions.
|
||||
enum DwarfCFI
|
||||
{
|
||||
DW_CFA_advance_loc = 0x40,
|
||||
DW_CFA_offset = 0x80,
|
||||
DW_CFA_restore = 0xc0,
|
||||
DW_CFA_nop = 0x00,
|
||||
DW_CFA_set_loc = 0x01,
|
||||
DW_CFA_advance_loc1 = 0x02,
|
||||
DW_CFA_advance_loc2 = 0x03,
|
||||
DW_CFA_advance_loc4 = 0x04,
|
||||
DW_CFA_offset_extended = 0x05,
|
||||
DW_CFA_restore_extended = 0x06,
|
||||
DW_CFA_undefined = 0x07,
|
||||
DW_CFA_same_value = 0x08,
|
||||
DW_CFA_register = 0x09,
|
||||
DW_CFA_remember_state = 0x0a,
|
||||
DW_CFA_restore_state = 0x0b,
|
||||
DW_CFA_def_cfa = 0x0c,
|
||||
DW_CFA_def_cfa_register = 0x0d,
|
||||
DW_CFA_def_cfa_offset = 0x0e,
|
||||
DW_CFA_def_cfa_expression = 0x0f,
|
||||
DW_CFA_expression = 0x10,
|
||||
DW_CFA_offset_extended_sf = 0x11,
|
||||
DW_CFA_def_cfa_sf = 0x12,
|
||||
DW_CFA_def_cfa_offset_sf = 0x13,
|
||||
DW_CFA_val_offset = 0x14,
|
||||
DW_CFA_val_offset_sf = 0x15,
|
||||
DW_CFA_val_expression = 0x16,
|
||||
|
||||
// Opcodes in this range are reserved for user extensions.
|
||||
DW_CFA_lo_user = 0x1c,
|
||||
DW_CFA_hi_user = 0x3f,
|
||||
|
||||
// SGI/MIPS specific.
|
||||
DW_CFA_MIPS_advance_loc8 = 0x1d,
|
||||
|
||||
// GNU extensions.
|
||||
DW_CFA_GNU_window_save = 0x2d,
|
||||
DW_CFA_GNU_args_size = 0x2e,
|
||||
DW_CFA_GNU_negative_offset_extended = 0x2f
|
||||
};
|
||||
|
||||
// Exception handling 'z' augmentation letters.
|
||||
enum DwarfZAugmentationCodes {
|
||||
// If the CFI augmentation string begins with 'z', then the CIE and FDE
|
||||
// have an augmentation data area just before the instructions, whose
|
||||
// contents are determined by the subsequent augmentation letters.
|
||||
DW_Z_augmentation_start = 'z',
|
||||
|
||||
// If this letter is present in a 'z' augmentation string, the CIE
|
||||
// augmentation data includes a pointer encoding, and the FDE
|
||||
// augmentation data includes a language-specific data area pointer,
|
||||
// represented using that encoding.
|
||||
DW_Z_has_LSDA = 'L',
|
||||
|
||||
// If this letter is present in a 'z' augmentation string, the CIE
|
||||
// augmentation data includes a pointer encoding, followed by a pointer
|
||||
// to a personality routine, represented using that encoding.
|
||||
DW_Z_has_personality_routine = 'P',
|
||||
|
||||
// If this letter is present in a 'z' augmentation string, the CIE
|
||||
// augmentation data includes a pointer encoding describing how the FDE's
|
||||
// initial location, address range, and DW_CFA_set_loc operands are
|
||||
// encoded.
|
||||
DW_Z_has_FDE_address_encoding = 'R',
|
||||
|
||||
// If this letter is present in a 'z' augmentation string, then code
|
||||
// addresses covered by FDEs that cite this CIE are signal delivery
|
||||
// trampolines. Return addresses of frames in trampolines should not be
|
||||
// adjusted as described in section 6.4.4 of the DWARF 3 spec.
|
||||
DW_Z_is_signal_trampoline = 'S'
|
||||
};
|
||||
|
||||
} // namespace lul
|
||||
|
||||
#endif // LulDwarfInt_h
|
242
tools/profiler/LulDwarfSummariser.cpp
Normal file
242
tools/profiler/LulDwarfSummariser.cpp
Normal file
@ -0,0 +1,242 @@
|
||||
/* -*- 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 {
|
||||
|
||||
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];
|
||||
snprintf(buf, sizeof(buf), "LUL Entry(%llx, %llu)\n",
|
||||
(unsigned long long int)aAddress,
|
||||
(unsigned long long int)aLength);
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
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, int aOldReg, intptr_t aOffset, bool aDeref)
|
||||
{
|
||||
aAddress += mTextBias;
|
||||
if (DEBUG_SUMMARISER) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"LUL 0x%llx old-r%d = %sr%d + %ld%s\n",
|
||||
(unsigned long long int)aAddress, aNewReg,
|
||||
aDeref ? "*(" : "", aOldReg, (long)aOffset, aDeref ? ")" : "");
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 (aDeref) {
|
||||
goto cant_summarise;
|
||||
}
|
||||
switch (aOldReg) {
|
||||
case DW_REG_ARM_R7: case DW_REG_ARM_R11:
|
||||
case DW_REG_ARM_R12: case DW_REG_ARM_R13:
|
||||
break;
|
||||
default:
|
||||
goto cant_summarise;
|
||||
}
|
||||
mCurrRules.mCfaExpr = LExpr(LExpr::NODEREF, aOldReg, aOffset);
|
||||
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: {
|
||||
// Check the aOldReg is valid.
|
||||
switch (aOldReg) {
|
||||
case DW_REG_CFA:
|
||||
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:
|
||||
break;
|
||||
default:
|
||||
goto cant_summarise;
|
||||
}
|
||||
// This is a new rule for one of r{7,11,12,13,14,15} and has a
|
||||
// representable offset. In particular the new value of r15 is
|
||||
// going to be the return address.
|
||||
LExpr expr = LExpr(aDeref ? LExpr::DEREF : LExpr::NODEREF,
|
||||
aOldReg, aOffset);
|
||||
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:
|
||||
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 == LExpr::UNKNOWN) {
|
||||
mCurrRules.mR7expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R7, 0);
|
||||
}
|
||||
if (mCurrRules.mR11expr.mHow == LExpr::UNKNOWN) {
|
||||
mCurrRules.mR11expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R11, 0);
|
||||
}
|
||||
if (mCurrRules.mR12expr.mHow == LExpr::UNKNOWN) {
|
||||
mCurrRules.mR12expr = LExpr(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(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 == LExpr::UNKNOWN) {
|
||||
mCurrRules.mR15expr = LExpr(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. In the 64 bit case
|
||||
// we need to check that aOffset will fit into an int32_t. In the
|
||||
// 32 bit case it is expected that the compiler will fold out the
|
||||
// test since it always succeeds.
|
||||
if (aNewReg == DW_REG_CFA) {
|
||||
// This is a rule that defines the CFA. The only forms we can
|
||||
// represent are: = SP+offset or = FP+offset.
|
||||
if (!aDeref && aOffset == (intptr_t)(int32_t)aOffset &&
|
||||
(aOldReg == DW_REG_INTEL_XSP || aOldReg == DW_REG_INTEL_XBP)) {
|
||||
mCurrRules.mCfaExpr = LExpr(LExpr::NODEREF, aOldReg, aOffset);
|
||||
} else {
|
||||
goto cant_summarise;
|
||||
}
|
||||
}
|
||||
else
|
||||
if ((aNewReg == DW_REG_INTEL_XSP ||
|
||||
aNewReg == DW_REG_INTEL_XBP || aNewReg == DW_REG_INTEL_XIP) &&
|
||||
(aOldReg == DW_REG_CFA ||
|
||||
aOldReg == DW_REG_INTEL_XSP ||
|
||||
aOldReg == DW_REG_INTEL_XBP || aOldReg == DW_REG_INTEL_XIP) &&
|
||||
aOffset == (intptr_t)(int32_t)aOffset) {
|
||||
// This is a new rule for SP, BP or the return address
|
||||
// respectively, and has a representable offset.
|
||||
LExpr expr = LExpr(aDeref ? LExpr::DEREF : LExpr::NODEREF,
|
||||
aOldReg, aOffset);
|
||||
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");
|
||||
}
|
||||
}
|
||||
else {
|
||||
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 == LExpr::UNKNOWN) {
|
||||
mCurrRules.mXspExpr = LExpr(LExpr::NODEREF, DW_REG_CFA, 0);
|
||||
}
|
||||
|
||||
// Also, gcc says "Undef" for BP when it is unchanged.
|
||||
if (mCurrRules.mXbpExpr.mHow == LExpr::UNKNOWN) {
|
||||
mCurrRules.mXbpExpr = LExpr(LExpr::NODEREF, DW_REG_INTEL_XBP, 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
# error "Unsupported arch"
|
||||
#endif
|
||||
|
||||
return;
|
||||
cant_summarise:
|
||||
if (0) {
|
||||
mLog("LUL can't summarise\n");
|
||||
}
|
||||
}
|
||||
|
||||
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
|
52
tools/profiler/LulDwarfSummariser.h
Normal file
52
tools/profiler/LulDwarfSummariser.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* -*- 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*));
|
||||
|
||||
void Entry(uintptr_t aAddress, uintptr_t aLength);
|
||||
void End();
|
||||
void Rule(uintptr_t aAddress,
|
||||
int aNewReg, int aOldReg, intptr_t aOffset, bool aDeref);
|
||||
|
||||
private:
|
||||
// The SecMap in which we park the finished summaries (RuleSets).
|
||||
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
|
1008
tools/profiler/LulElf.cpp
Normal file
1008
tools/profiler/LulElf.cpp
Normal file
File diff suppressed because it is too large
Load Diff
66
tools/profiler/LulElfExt.h
Normal file
66
tools/profiler/LulElfExt.h
Normal file
@ -0,0 +1,66 @@
|
||||
/* -*- 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.
|
||||
|
||||
// This file is derived from the following files in
|
||||
// toolkit/crashreporter/google-breakpad:
|
||||
// src/common/linux/dump_symbols.h
|
||||
|
||||
#ifndef LulElfExt_h
|
||||
#define LulElfExt_h
|
||||
|
||||
// These two functions are the external interface to the
|
||||
// ELF/Dwarf/EXIDX reader.
|
||||
|
||||
#include "LulMainInt.h"
|
||||
|
||||
using lul::SecMap;
|
||||
|
||||
namespace lul {
|
||||
|
||||
// Find all the unwind information in OBJ_FILE, an ELF executable
|
||||
// or shared library, and add it to SMAP.
|
||||
bool ReadSymbolData(const std::string& obj_file,
|
||||
const std::vector<std::string>& debug_dirs,
|
||||
SecMap* smap, void* rx_avma,
|
||||
void (*log)(const char*));
|
||||
|
||||
// The same as ReadSymbolData, except that OBJ_FILE is assumed to
|
||||
// point to a mapped-in image of OBJ_FILENAME.
|
||||
bool ReadSymbolDataInternal(const uint8_t* obj_file,
|
||||
const std::string& obj_filename,
|
||||
const std::vector<std::string>& debug_dirs,
|
||||
SecMap* smap, void* rx_avma,
|
||||
void (*log)(const char*));
|
||||
|
||||
} // namespace lul
|
||||
|
||||
#endif // LulElfExt_h
|
234
tools/profiler/LulElfInt.h
Normal file
234
tools/profiler/LulElfInt.h
Normal file
@ -0,0 +1,234 @@
|
||||
/* -*- 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, 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.
|
||||
|
||||
// This file is derived from the following files in
|
||||
// toolkit/crashreporter/google-breakpad:
|
||||
// src/common/android/include/elf.h
|
||||
// src/common/linux/elfutils.h
|
||||
// src/common/linux/file_id.h
|
||||
// src/common/linux/elfutils-inl.h
|
||||
|
||||
#ifndef LulElfInt_h
|
||||
#define LulElfInt_h
|
||||
|
||||
// This header defines functions etc internal to the ELF reader. It
|
||||
// should not be included outside of LulElf.cpp.
|
||||
|
||||
#include <elf.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
#include "LulPlatformMacros.h"
|
||||
|
||||
|
||||
// (derived from)
|
||||
// elfutils.h: Utilities for dealing with ELF files.
|
||||
//
|
||||
|
||||
#if defined(LUL_OS_android)
|
||||
|
||||
// From toolkit/crashreporter/google-breakpad/src/common/android/include/elf.h
|
||||
// The Android headers don't always define this constant.
|
||||
#ifndef EM_X86_64
|
||||
#define EM_X86_64 62
|
||||
#endif
|
||||
|
||||
#ifndef EM_PPC64
|
||||
#define EM_PPC64 21
|
||||
#endif
|
||||
|
||||
#ifndef EM_S390
|
||||
#define EM_S390 22
|
||||
#endif
|
||||
|
||||
#ifndef NT_GNU_BUILD_ID
|
||||
#define NT_GNU_BUILD_ID 3
|
||||
#endif
|
||||
|
||||
#define ElfW(type) _ElfW (Elf, ELFSIZE, type)
|
||||
#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
|
||||
#define _ElfW_1(e,w,t) e##w##t
|
||||
|
||||
//FIXME
|
||||
extern "C" {
|
||||
extern char* basename(const char* path);
|
||||
};
|
||||
#else
|
||||
|
||||
# include <link.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace lul {
|
||||
|
||||
// Traits classes so consumers can write templatized code to deal
|
||||
// with specific ELF bits.
|
||||
struct ElfClass32 {
|
||||
typedef Elf32_Addr Addr;
|
||||
typedef Elf32_Ehdr Ehdr;
|
||||
typedef Elf32_Nhdr Nhdr;
|
||||
typedef Elf32_Phdr Phdr;
|
||||
typedef Elf32_Shdr Shdr;
|
||||
typedef Elf32_Half Half;
|
||||
typedef Elf32_Off Off;
|
||||
typedef Elf32_Word Word;
|
||||
static const int kClass = ELFCLASS32;
|
||||
static const size_t kAddrSize = sizeof(Elf32_Addr);
|
||||
};
|
||||
|
||||
struct ElfClass64 {
|
||||
typedef Elf64_Addr Addr;
|
||||
typedef Elf64_Ehdr Ehdr;
|
||||
typedef Elf64_Nhdr Nhdr;
|
||||
typedef Elf64_Phdr Phdr;
|
||||
typedef Elf64_Shdr Shdr;
|
||||
typedef Elf64_Half Half;
|
||||
typedef Elf64_Off Off;
|
||||
typedef Elf64_Word Word;
|
||||
static const int kClass = ELFCLASS64;
|
||||
static const size_t kAddrSize = sizeof(Elf64_Addr);
|
||||
};
|
||||
|
||||
bool IsValidElf(const void* elf_header);
|
||||
int ElfClass(const void* elf_base);
|
||||
|
||||
// Attempt to find a section named |section_name| of type |section_type|
|
||||
// in the ELF binary data at |elf_mapped_base|. On success, returns true
|
||||
// and sets |*section_start| to point to the start of the section data,
|
||||
// and |*section_size| to the size of the section's data. If |elfclass|
|
||||
// is not NULL, set |*elfclass| to the ELF file 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);
|
||||
|
||||
// Internal helper method, exposed for convenience for callers
|
||||
// that already have more info.
|
||||
template<typename ElfClass>
|
||||
const typename ElfClass::Shdr*
|
||||
FindElfSectionByName(const char* name,
|
||||
typename ElfClass::Word section_type,
|
||||
const typename ElfClass::Shdr* sections,
|
||||
const char* section_names,
|
||||
const char* names_end,
|
||||
int nsection);
|
||||
|
||||
// Attempt to find the first segment of type |segment_type| in the ELF
|
||||
// binary data at |elf_mapped_base|. On success, returns true and sets
|
||||
// |*segment_start| to point to the start of the segment data, and
|
||||
// and |*segment_size| to the size of the segment's data. If |elfclass|
|
||||
// is not NULL, set |*elfclass| to the ELF file class.
|
||||
bool FindElfSegment(const void *elf_mapped_base,
|
||||
uint32_t segment_type,
|
||||
const void **segment_start,
|
||||
int *segment_size,
|
||||
int *elfclass);
|
||||
|
||||
// Convert an offset from an Elf header into a pointer to the mapped
|
||||
// address in the current process. Takes an extra template parameter
|
||||
// to specify the return type to avoid having to dynamic_cast the
|
||||
// result.
|
||||
template<typename ElfClass, typename T>
|
||||
const T*
|
||||
GetOffset(const typename ElfClass::Ehdr* elf_header,
|
||||
typename ElfClass::Off offset);
|
||||
|
||||
|
||||
// (derived from)
|
||||
// file_id.h: Return a unique identifier for a file
|
||||
//
|
||||
|
||||
static const size_t kMDGUIDSize = sizeof(MDGUID);
|
||||
|
||||
class FileID {
|
||||
public:
|
||||
|
||||
// Load the identifier for the elf file mapped into memory at |base| into
|
||||
// |identifier|. Return false if the identifier could not be created for the
|
||||
// file.
|
||||
static bool ElfFileIdentifierFromMappedFile(const void* base,
|
||||
uint8_t identifier[kMDGUIDSize]);
|
||||
|
||||
// Convert the |identifier| data to a NULL terminated string. The string will
|
||||
// be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE).
|
||||
// The |buffer| should be at least 37 bytes long to receive all of the data
|
||||
// and termination. Shorter buffers will contain truncated data.
|
||||
static void ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
|
||||
char* buffer, int buffer_length);
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename ElfClass, typename T>
|
||||
const T* GetOffset(const typename ElfClass::Ehdr* elf_header,
|
||||
typename ElfClass::Off offset) {
|
||||
return reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(elf_header) +
|
||||
offset);
|
||||
}
|
||||
|
||||
template<typename ElfClass>
|
||||
const typename ElfClass::Shdr* FindElfSectionByName(
|
||||
const char* name,
|
||||
typename ElfClass::Word section_type,
|
||||
const typename ElfClass::Shdr* sections,
|
||||
const char* section_names,
|
||||
const char* names_end,
|
||||
int nsection) {
|
||||
MOZ_ASSERT(name != NULL);
|
||||
MOZ_ASSERT(sections != NULL);
|
||||
MOZ_ASSERT(nsection > 0);
|
||||
|
||||
int name_len = strlen(name);
|
||||
if (name_len == 0)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < nsection; ++i) {
|
||||
const char* section_name = section_names + sections[i].sh_name;
|
||||
if (sections[i].sh_type == section_type &&
|
||||
names_end - section_name >= name_len + 1 &&
|
||||
strcmp(name, section_name) == 0) {
|
||||
return sections + i;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace lul
|
||||
|
||||
|
||||
// And finally, the external interface, offered to LulMain.cpp
|
||||
#include "LulElfExt.h"
|
||||
|
||||
#endif // LulElfInt_h
|
690
tools/profiler/LulExidx.cpp
Normal file
690
tools/profiler/LulExidx.cpp
Normal file
@ -0,0 +1,690 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
|
||||
/* libunwind - a platform-independent unwind library
|
||||
Copyright 2011 Linaro Limited
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
|
||||
// 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.
|
||||
|
||||
|
||||
// Derived from libunwind, with extensive modifications.
|
||||
|
||||
// This file translates EXIDX unwind information into the same format
|
||||
// that LUL uses for CFI information. Hence LUL's CFI unwinding
|
||||
// abilities also become usable for EXIDX.
|
||||
//
|
||||
// See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A
|
||||
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
|
||||
|
||||
// EXIDX data is presented in two parts:
|
||||
//
|
||||
// * an index table. This contains two words per routine,
|
||||
// the first of which identifies the routine, and the second
|
||||
// of which is a reference to the unwind bytecode. If the
|
||||
// bytecode is very compact -- 3 bytes or less -- it can be
|
||||
// stored directly in the second word.
|
||||
//
|
||||
// * an area containing the unwind bytecodes.
|
||||
//
|
||||
// General flow is: ExceptionTableInfo::Start iterates over all
|
||||
// of the index table entries (pairs). For each entry, it:
|
||||
//
|
||||
// * calls ExceptionTableInfo::ExtabEntryExtract to copy the bytecode
|
||||
// out into an intermediate buffer.
|
||||
|
||||
// * uses ExceptionTableInfo::ExtabEntryDecode to parse the intermediate
|
||||
// buffer. Each bytecode instruction is bundled into a
|
||||
// arm_ex_to_module::extab_data structure, and handed to ..
|
||||
//
|
||||
// * .. ARMExToModule::ImproveStackFrame, which in turn hands it to
|
||||
// ARMExToModule::TranslateCmd, and that generates the pseudo-CFI
|
||||
// records that Breakpad stores.
|
||||
|
||||
// This file is derived from the following files in
|
||||
// toolkit/crashreporter/google-breakpad:
|
||||
// src/common/arm_ex_to_module.cc
|
||||
// src/common/arm_ex_reader.cc
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
|
||||
#include "LulExidxExt.h"
|
||||
|
||||
|
||||
#define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f)
|
||||
#define ARM_EXBUF_COUNT(x) ((x) & 0x0f)
|
||||
#define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
|
||||
|
||||
namespace lul {
|
||||
|
||||
// Translate command from extab_data to command for Module.
|
||||
int ARMExToModule::TranslateCmd(const struct extab_data* edata,
|
||||
LExpr& vsp) {
|
||||
int ret = 0;
|
||||
switch (edata->cmd) {
|
||||
case ARM_EXIDX_CMD_FINISH:
|
||||
/* Copy LR to PC if there isn't currently a rule for PC in force. */
|
||||
if (curr_rules_.mR15expr.mHow == LExpr::UNKNOWN) {
|
||||
if (curr_rules_.mR14expr.mHow == LExpr::UNKNOWN) {
|
||||
curr_rules_.mR15expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0);
|
||||
} else {
|
||||
curr_rules_.mR15expr = curr_rules_.mR14expr;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_SUB_FROM_VSP:
|
||||
vsp = vsp.add_delta(- static_cast<long>(edata->data));
|
||||
break;
|
||||
case ARM_EXIDX_CMD_ADD_TO_VSP:
|
||||
vsp = vsp.add_delta(static_cast<long>(edata->data));
|
||||
break;
|
||||
case ARM_EXIDX_CMD_REG_POP:
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
if (edata->data & (1 << i)) {
|
||||
// See if we're summarising for int register |i|. If so,
|
||||
// describe how to pull it off the stack. The cast of |i| is
|
||||
// a bit of a kludge but works because DW_REG_ARM_Rn has the
|
||||
// value |n|, for 0 <= |n| <= 15 -- that is, for the ARM
|
||||
// general-purpose registers.
|
||||
LExpr* regI_exprP = curr_rules_.ExprForRegno((DW_REG_NUMBER)i);
|
||||
if (regI_exprP) {
|
||||
*regI_exprP = vsp.deref();
|
||||
}
|
||||
vsp = vsp.add_delta(4);
|
||||
}
|
||||
}
|
||||
/* Set cfa in case the SP got popped. */
|
||||
if (edata->data & (1 << 13)) {
|
||||
vsp = curr_rules_.mR13expr;
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_REG_TO_SP: {
|
||||
MOZ_ASSERT (edata->data < 16);
|
||||
int reg_no = edata->data;
|
||||
// Same comment as above, re the casting of |reg_no|, applies.
|
||||
LExpr* reg_exprP = curr_rules_.ExprForRegno((DW_REG_NUMBER)reg_no);
|
||||
if (reg_exprP) {
|
||||
if (reg_exprP->mHow == LExpr::UNKNOWN) {
|
||||
curr_rules_.mR13expr = LExpr(LExpr::NODEREF, reg_no, 0);
|
||||
} else {
|
||||
curr_rules_.mR13expr = *reg_exprP;
|
||||
}
|
||||
vsp = curr_rules_.mR13expr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ARM_EXIDX_CMD_VFP_POP:
|
||||
/* Don't recover VFP registers, but be sure to adjust the stack
|
||||
pointer. */
|
||||
for (unsigned int i = ARM_EXBUF_START(edata->data);
|
||||
i <= ARM_EXBUF_END(edata->data); i++) {
|
||||
vsp = vsp.add_delta(8);
|
||||
}
|
||||
if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) {
|
||||
vsp = vsp.add_delta(4);
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_WREG_POP:
|
||||
for (unsigned int i = ARM_EXBUF_START(edata->data);
|
||||
i <= ARM_EXBUF_END(edata->data); i++) {
|
||||
vsp = vsp.add_delta(8);
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_WCGR_POP:
|
||||
// Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4"
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
if (edata->data & (1 << i)) {
|
||||
vsp = vsp.add_delta(4);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ARM_EXIDX_CMD_REFUSED:
|
||||
case ARM_EXIDX_CMD_RESERVED:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) {
|
||||
// Here we are effectively reinitialising the EXIDX summariser for a
|
||||
// new code address range. smap_ stays unchanged. All other fields
|
||||
// are reinitialised.
|
||||
vsp_ = LExpr(LExpr::NODEREF, DW_REG_ARM_R13, 0);
|
||||
(void) new (&curr_rules_) RuleSet();
|
||||
curr_rules_.mAddr = (uintptr_t)addr;
|
||||
curr_rules_.mLen = (uintptr_t)size;
|
||||
if (0) {
|
||||
char buf[100];
|
||||
sprintf(buf, " AddStackFrame %llx .. %llx",
|
||||
(uint64_t)addr, (uint64_t)(addr + size - 1));
|
||||
log_(buf);
|
||||
}
|
||||
}
|
||||
|
||||
int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) {
|
||||
return TranslateCmd(edata, vsp_) ;
|
||||
}
|
||||
|
||||
void ARMExToModule::DeleteStackFrame() {
|
||||
}
|
||||
|
||||
void ARMExToModule::SubmitStackFrame() {
|
||||
// JRS: I'm really not sure what this means, or if it is necessary
|
||||
// return address always winds up in pc
|
||||
//stack_frame_entry_->initial_rules[ustr__ZDra()] // ".ra"
|
||||
// = stack_frame_entry_->initial_rules[ustr__pc()];
|
||||
// maybe don't need to do anything here?
|
||||
|
||||
// the final value of vsp is the new value of sp
|
||||
curr_rules_.mR13expr = vsp_;
|
||||
|
||||
// Finally, add the completed RuleSet to the SecMap
|
||||
if (curr_rules_.mLen > 0) {
|
||||
|
||||
// Futz with the rules for r4 .. r11 in the same way as happens
|
||||
// with the CFI summariser:
|
||||
/* 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 (curr_rules_.mR7expr.mHow == LExpr::UNKNOWN) {
|
||||
curr_rules_.mR7expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R7, 0);
|
||||
}
|
||||
if (curr_rules_.mR11expr.mHow == LExpr::UNKNOWN) {
|
||||
curr_rules_.mR11expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R11, 0);
|
||||
}
|
||||
if (curr_rules_.mR12expr.mHow == LExpr::UNKNOWN) {
|
||||
curr_rules_.mR12expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R12, 0);
|
||||
}
|
||||
if (curr_rules_.mR14expr.mHow == LExpr::UNKNOWN) {
|
||||
curr_rules_.mR14expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0);
|
||||
}
|
||||
|
||||
// And add them
|
||||
smap_->AddRuleSet(&curr_rules_);
|
||||
|
||||
if (0) {
|
||||
curr_rules_.Print(log_);
|
||||
}
|
||||
if (0) {
|
||||
char buf[100];
|
||||
sprintf(buf, " SubmitStackFrame %llx .. %llx",
|
||||
(uint64_t)curr_rules_.mAddr,
|
||||
(uint64_t)(curr_rules_.mAddr + curr_rules_.mLen - 1));
|
||||
log_(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define ARM_EXIDX_CANT_UNWIND 0x00000001
|
||||
#define ARM_EXIDX_COMPACT 0x80000000
|
||||
#define ARM_EXTBL_OP_FINISH 0xb0
|
||||
#define ARM_EXIDX_TABLE_LIMIT (255*4)
|
||||
|
||||
using lul::ARM_EXIDX_CMD_FINISH;
|
||||
using lul::ARM_EXIDX_CMD_SUB_FROM_VSP;
|
||||
using lul::ARM_EXIDX_CMD_ADD_TO_VSP;
|
||||
using lul::ARM_EXIDX_CMD_REG_POP;
|
||||
using lul::ARM_EXIDX_CMD_REG_TO_SP;
|
||||
using lul::ARM_EXIDX_CMD_VFP_POP;
|
||||
using lul::ARM_EXIDX_CMD_WREG_POP;
|
||||
using lul::ARM_EXIDX_CMD_WCGR_POP;
|
||||
using lul::ARM_EXIDX_CMD_RESERVED;
|
||||
using lul::ARM_EXIDX_CMD_REFUSED;
|
||||
using lul::exidx_entry;
|
||||
using lul::ARM_EXIDX_VFP_SHIFT_16;
|
||||
using lul::ARM_EXIDX_VFP_FSTMD;
|
||||
using lul::MemoryRange;
|
||||
|
||||
|
||||
static void* Prel31ToAddr(const void* addr)
|
||||
{
|
||||
uint32_t offset32 = *reinterpret_cast<const uint32_t*>(addr);
|
||||
// sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions
|
||||
// 63:31 inclusive.
|
||||
uint64_t offset64 = offset32;
|
||||
if (offset64 & (1ULL << 30))
|
||||
offset64 |= 0xFFFFFFFF80000000ULL;
|
||||
else
|
||||
offset64 &= 0x000000007FFFFFFFULL;
|
||||
return ((char*)addr) + (uintptr_t)offset64;
|
||||
}
|
||||
|
||||
|
||||
// Extract unwind bytecode for the function denoted by |entry| into |buf|,
|
||||
// and return the number of bytes of |buf| written, along with a code
|
||||
// indicating the outcome.
|
||||
|
||||
ExceptionTableInfo::ExExtractResult
|
||||
ExceptionTableInfo::ExtabEntryExtract(const struct exidx_entry* entry,
|
||||
uint8_t* buf, size_t buf_size,
|
||||
/*OUT*/size_t* buf_used)
|
||||
{
|
||||
MemoryRange mr_out(buf, buf_size);
|
||||
|
||||
*buf_used = 0;
|
||||
|
||||
# define PUT_BUF_U8(_byte) \
|
||||
do { if (!mr_out.Covers(*buf_used, 1)) return ExOutBufOverflow; \
|
||||
buf[(*buf_used)++] = (_byte); } while (0)
|
||||
|
||||
# define GET_EX_U32(_lval, _addr, _sec_mr) \
|
||||
do { if (!(_sec_mr).Covers(reinterpret_cast<const uint8_t*>(_addr) \
|
||||
- (_sec_mr).data(), 4)) \
|
||||
return ExInBufOverflow; \
|
||||
(_lval) = *(reinterpret_cast<const uint32_t*>(_addr)); } while (0)
|
||||
|
||||
# define GET_EXIDX_U32(_lval, _addr) \
|
||||
GET_EX_U32(_lval, _addr, mr_exidx_)
|
||||
# define GET_EXTAB_U32(_lval, _addr) \
|
||||
GET_EX_U32(_lval, _addr, mr_extab_)
|
||||
|
||||
uint32_t data;
|
||||
GET_EXIDX_U32(data, &entry->data);
|
||||
|
||||
// A function can be marked CANT_UNWIND if (eg) it is known to be
|
||||
// at the bottom of the stack.
|
||||
if (data == ARM_EXIDX_CANT_UNWIND)
|
||||
return ExCantUnwind;
|
||||
|
||||
uint32_t pers; // personality number
|
||||
uint32_t extra; // number of extra data words required
|
||||
uint32_t extra_allowed; // number of extra data words allowed
|
||||
uint32_t* extbl_data; // the handler entry, if not inlined
|
||||
|
||||
if (data & ARM_EXIDX_COMPACT) {
|
||||
// The handler table entry has been inlined into the index table entry.
|
||||
// In this case it can only be an ARM-defined compact model, since
|
||||
// bit 31 is 1. Only personalities 0, 1 and 2 are defined for the
|
||||
// ARM compact model, but 1 and 2 are "Long format" and may require
|
||||
// extra data words. Hence the allowable personalities here are:
|
||||
// personality 0, in which case 'extra' has no meaning
|
||||
// personality 1, with zero extra words
|
||||
// personality 2, with zero extra words
|
||||
extbl_data = nullptr;
|
||||
pers = (data >> 24) & 0x0F;
|
||||
extra = (data >> 16) & 0xFF;
|
||||
extra_allowed = 0;
|
||||
}
|
||||
else {
|
||||
// The index table entry is a pointer to the handler entry. Note
|
||||
// that Prel31ToAddr will read the given address, but we already
|
||||
// range-checked above.
|
||||
extbl_data = reinterpret_cast<uint32_t*>(Prel31ToAddr(&entry->data));
|
||||
GET_EXTAB_U32(data, extbl_data);
|
||||
if (!(data & ARM_EXIDX_COMPACT)) {
|
||||
// This denotes a "generic model" handler. That will involve
|
||||
// executing arbitary machine code, which is something we
|
||||
// can't represent here; hence reject it.
|
||||
return ExCantRepresent;
|
||||
}
|
||||
// So we have a compact model representation. Again, 3 possible
|
||||
// personalities, but this time up to 255 allowable extra words.
|
||||
pers = (data >> 24) & 0x0F;
|
||||
extra = (data >> 16) & 0xFF;
|
||||
extra_allowed = 255;
|
||||
extbl_data++;
|
||||
}
|
||||
|
||||
// Now look at the the handler table entry. The first word is
|
||||
// |data| and subsequent words start at |*extbl_data|. The number
|
||||
// of extra words to use is |extra|, provided that the personality
|
||||
// allows extra words. Even if it does, none may be available --
|
||||
// extra_allowed is the maximum number of extra words allowed. */
|
||||
if (pers == 0) {
|
||||
// "Su16" in the documentation -- 3 unwinding insn bytes
|
||||
// |extra| has no meaning here; instead that byte is an unwind-info byte
|
||||
PUT_BUF_U8(data >> 16);
|
||||
PUT_BUF_U8(data >> 8);
|
||||
PUT_BUF_U8(data);
|
||||
}
|
||||
else if ((pers == 1 || pers == 2) && extra <= extra_allowed) {
|
||||
// "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes,
|
||||
// and up to 255 extra words.
|
||||
PUT_BUF_U8(data >> 8);
|
||||
PUT_BUF_U8(data);
|
||||
for (uint32_t j = 0; j < extra; j++) {
|
||||
GET_EXTAB_U32(data, extbl_data);
|
||||
extbl_data++;
|
||||
PUT_BUF_U8(data >> 24);
|
||||
PUT_BUF_U8(data >> 16);
|
||||
PUT_BUF_U8(data >> 8);
|
||||
PUT_BUF_U8(data >> 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The entry is invalid.
|
||||
return ExInvalid;
|
||||
}
|
||||
|
||||
// Make sure the entry is terminated with "FINISH"
|
||||
if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH)
|
||||
PUT_BUF_U8(ARM_EXTBL_OP_FINISH);
|
||||
|
||||
return ExSuccess;
|
||||
|
||||
# undef GET_EXTAB_U32
|
||||
# undef GET_EXIDX_U32
|
||||
# undef GET_U32
|
||||
# undef PUT_BUF_U8
|
||||
}
|
||||
|
||||
|
||||
// Take the unwind information extracted by ExtabEntryExtract
|
||||
// and parse it into frame-unwind instructions. These are as
|
||||
// specified in "Table 4, ARM-defined frame-unwinding instructions"
|
||||
// in the specification document detailed in comments at the top
|
||||
// of this file.
|
||||
//
|
||||
// This reads from |buf[0, +data_size)|. It checks for overruns of
|
||||
// the input buffer and returns a negative value if that happens, or
|
||||
// for any other failure cases. It returns zero in case of success.
|
||||
int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size)
|
||||
{
|
||||
if (buf == nullptr || buf_size == 0)
|
||||
return -1;
|
||||
|
||||
MemoryRange mr_in(buf, buf_size);
|
||||
const uint8_t* buf_initially = buf;
|
||||
|
||||
# define GET_BUF_U8(_lval) \
|
||||
do { if (!mr_in.Covers(buf - buf_initially, 1)) return -1; \
|
||||
(_lval) = *(buf++); } while (0)
|
||||
|
||||
const uint8_t* end = buf + buf_size;
|
||||
|
||||
while (buf < end) {
|
||||
struct lul::extab_data edata;
|
||||
memset(&edata, 0, sizeof(edata));
|
||||
|
||||
uint8_t op;
|
||||
GET_BUF_U8(op);
|
||||
if ((op & 0xc0) == 0x00) {
|
||||
// vsp = vsp + (xxxxxx << 2) + 4
|
||||
edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
|
||||
edata.data = (((int)op & 0x3f) << 2) + 4;
|
||||
}
|
||||
else if ((op & 0xc0) == 0x40) {
|
||||
// vsp = vsp - (xxxxxx << 2) - 4
|
||||
edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP;
|
||||
edata.data = (((int)op & 0x3f) << 2) + 4;
|
||||
}
|
||||
else if ((op & 0xf0) == 0x80) {
|
||||
uint8_t op2;
|
||||
GET_BUF_U8(op2);
|
||||
if (op == 0x80 && op2 == 0x00) {
|
||||
// Refuse to unwind
|
||||
edata.cmd = ARM_EXIDX_CMD_REFUSED;
|
||||
} else {
|
||||
// Pop up to 12 integer registers under masks {r15-r12},{r11-r4}
|
||||
edata.cmd = ARM_EXIDX_CMD_REG_POP;
|
||||
edata.data = ((op & 0xf) << 8) | op2;
|
||||
edata.data = edata.data << 4;
|
||||
}
|
||||
}
|
||||
else if ((op & 0xf0) == 0x90) {
|
||||
if (op == 0x9d || op == 0x9f) {
|
||||
// 9d: Reserved as prefix for ARM register to register moves
|
||||
// 9f: Reserved as perfix for Intel Wireless MMX reg to reg moves
|
||||
edata.cmd = ARM_EXIDX_CMD_RESERVED;
|
||||
} else {
|
||||
// Set vsp = r[nnnn]
|
||||
edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
|
||||
edata.data = op & 0x0f;
|
||||
}
|
||||
}
|
||||
else if ((op & 0xf0) == 0xa0) {
|
||||
// Pop r4 to r[4+nnn], or
|
||||
// Pop r4 to r[4+nnn] and r14 or
|
||||
unsigned end = (op & 0x07);
|
||||
edata.data = (1 << (end + 1)) - 1;
|
||||
edata.data = edata.data << 4;
|
||||
if (op & 0x08) edata.data |= 1 << 14;
|
||||
edata.cmd = ARM_EXIDX_CMD_REG_POP;
|
||||
}
|
||||
else if (op == ARM_EXTBL_OP_FINISH) {
|
||||
// Finish
|
||||
edata.cmd = ARM_EXIDX_CMD_FINISH;
|
||||
buf = end;
|
||||
}
|
||||
else if (op == 0xb1) {
|
||||
uint8_t op2;
|
||||
GET_BUF_U8(op2);
|
||||
if (op2 == 0 || (op2 & 0xf0)) {
|
||||
// Spare
|
||||
edata.cmd = ARM_EXIDX_CMD_RESERVED;
|
||||
} else {
|
||||
// Pop integer registers under mask {r3,r2,r1,r0}
|
||||
edata.cmd = ARM_EXIDX_CMD_REG_POP;
|
||||
edata.data = op2 & 0x0f;
|
||||
}
|
||||
}
|
||||
else if (op == 0xb2) {
|
||||
// vsp = vsp + 0x204 + (uleb128 << 2)
|
||||
uint64_t offset = 0;
|
||||
uint8_t byte, shift = 0;
|
||||
do {
|
||||
GET_BUF_U8(byte);
|
||||
offset |= (byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
} while ((byte & 0x80) && buf < end);
|
||||
edata.data = offset * 4 + 0x204;
|
||||
edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
|
||||
}
|
||||
else if (op == 0xb3 || op == 0xc8 || op == 0xc9) {
|
||||
// b3: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDX-ishly
|
||||
// c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly
|
||||
// c9: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDD-ishly
|
||||
edata.cmd = ARM_EXIDX_CMD_VFP_POP;
|
||||
GET_BUF_U8(edata.data);
|
||||
if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16;
|
||||
if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD;
|
||||
}
|
||||
else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) {
|
||||
// b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly
|
||||
// d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly
|
||||
edata.cmd = ARM_EXIDX_CMD_VFP_POP;
|
||||
edata.data = 0x80 | (op & 0x07);
|
||||
if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD;
|
||||
}
|
||||
else if (op >= 0xc0 && op <= 0xc5) {
|
||||
// Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7
|
||||
edata.cmd = ARM_EXIDX_CMD_WREG_POP;
|
||||
edata.data = 0xa0 | (op & 0x07);
|
||||
}
|
||||
else if (op == 0xc6) {
|
||||
// Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc]
|
||||
edata.cmd = ARM_EXIDX_CMD_WREG_POP;
|
||||
GET_BUF_U8(edata.data);
|
||||
}
|
||||
else if (op == 0xc7) {
|
||||
uint8_t op2;
|
||||
GET_BUF_U8(op2);
|
||||
if (op2 == 0 || (op2 & 0xf0)) {
|
||||
// Spare
|
||||
edata.cmd = ARM_EXIDX_CMD_RESERVED;
|
||||
} else {
|
||||
// Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}
|
||||
edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
|
||||
edata.data = op2 & 0x0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Spare
|
||||
edata.cmd = ARM_EXIDX_CMD_RESERVED;
|
||||
}
|
||||
|
||||
int ret = handler_->ImproveStackFrame(&edata);
|
||||
if (ret < 0) return ret;
|
||||
}
|
||||
return 0;
|
||||
|
||||
# undef GET_BUF_U8
|
||||
}
|
||||
|
||||
void ExceptionTableInfo::Start()
|
||||
{
|
||||
const struct exidx_entry* start
|
||||
= reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data());
|
||||
const struct exidx_entry* end
|
||||
= reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data()
|
||||
+ mr_exidx_.length());
|
||||
|
||||
// Iterate over each of the EXIDX entries (pairs of 32-bit words).
|
||||
// These occupy the entire .exidx section.
|
||||
for (const struct exidx_entry* entry = start; entry < end; ++entry) {
|
||||
|
||||
// Figure out the code address range that this table entry is
|
||||
// associated with.
|
||||
//
|
||||
// I don't claim to understand the biasing here. It appears that
|
||||
// (Prel31ToAddr(&entry->addr))
|
||||
// - mapping_addr_ + loading_addr_) & 0x7fffffff
|
||||
// produces a SVMA. Adding the text_bias_ gives plausible AVMAs.
|
||||
uint32_t svma = (reinterpret_cast<char*>(Prel31ToAddr(&entry->addr))
|
||||
- mapping_addr_ + loading_addr_) & 0x7fffffff;
|
||||
uint32_t next_svma;
|
||||
if (entry < end - 1) {
|
||||
next_svma = (reinterpret_cast<char*>(Prel31ToAddr(&((entry + 1)->addr)))
|
||||
- mapping_addr_ + loading_addr_) & 0x7fffffff;
|
||||
} else {
|
||||
// This is the last EXIDX entry in the sequence, so we don't
|
||||
// have an address for the start of the next function, to limit
|
||||
// this one. Instead use the address of the last byte of the
|
||||
// text section associated with this .exidx section, that we
|
||||
// have been given. So as to avoid junking up the CFI unwind
|
||||
// tables with absurdly large address ranges in the case where
|
||||
// text_last_svma_ is wrong, only use the value if it is nonzero
|
||||
// and within one page of |svma|. Otherwise assume a length of 1.
|
||||
//
|
||||
// In some cases, gcc has been observed to finish the exidx
|
||||
// section with an entry of length 1 marked CANT_UNWIND,
|
||||
// presumably exactly for the purpose of giving a definite
|
||||
// length for the last real entry, without having to look at
|
||||
// text segment boundaries.
|
||||
bool plausible = false;
|
||||
next_svma = svma + 1;
|
||||
if (text_last_svma_ != 0) {
|
||||
uint32_t maybe_next_svma = text_last_svma_ + 1;
|
||||
if (maybe_next_svma > svma && maybe_next_svma - svma <= 4096) {
|
||||
next_svma = maybe_next_svma;
|
||||
plausible = true;
|
||||
}
|
||||
}
|
||||
if (!plausible) {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"ExceptionTableInfo: implausible EXIDX last entry size %d"
|
||||
"; using 1 instead.", (int32_t)(text_last_svma_ - svma));
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
log_(buf);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the unwind info into |buf|. This might fail for
|
||||
// various reasons. It involves reading both the .exidx and
|
||||
// .extab sections. All accesses to those sections are
|
||||
// bounds-checked.
|
||||
uint8_t buf[ARM_EXIDX_TABLE_LIMIT];
|
||||
size_t buf_used = 0;
|
||||
ExExtractResult res = ExtabEntryExtract(entry, buf, sizeof(buf), &buf_used);
|
||||
if (res != ExSuccess) {
|
||||
// Couldn't extract the unwind info, for some reason. Move on.
|
||||
switch (res) {
|
||||
case ExInBufOverflow:
|
||||
log_("ExtabEntryExtract: .exidx/.extab section overrun");
|
||||
break;
|
||||
case ExOutBufOverflow:
|
||||
log_("ExtabEntryExtract: bytecode buffer overflow");
|
||||
break;
|
||||
case ExCantUnwind:
|
||||
log_("ExtabEntryExtract: function is marked CANT_UNWIND");
|
||||
break;
|
||||
case ExCantRepresent:
|
||||
log_("ExtabEntryExtract: bytecode can't be represented");
|
||||
break;
|
||||
case ExInvalid:
|
||||
log_("ExtabEntryExtract: index table entry is invalid");
|
||||
break;
|
||||
default: {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"ExtabEntryExtract: unknown error: %d", (int)res);
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
log_(buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finally, work through the unwind instructions in |buf| and
|
||||
// create CFI entries that Breakpad can use. This can also fail.
|
||||
// First, add a new stack frame entry, into which ExtabEntryDecode
|
||||
// will write the CFI entries.
|
||||
handler_->AddStackFrame(svma + text_bias_, next_svma - svma);
|
||||
int ret = ExtabEntryDecode(buf, buf_used);
|
||||
if (ret < 0) {
|
||||
handler_->DeleteStackFrame();
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"ExtabEntryDecode: failed with error code: %d", ret);
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
log_(buf);
|
||||
continue;
|
||||
}
|
||||
handler_->SubmitStackFrame();
|
||||
} /* iterating over .exidx */
|
||||
}
|
||||
|
||||
} // namespace lul
|
233
tools/profiler/LulExidxExt.h
Normal file
233
tools/profiler/LulExidxExt.h
Normal file
@ -0,0 +1,233 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
|
||||
/* libunwind - a platform-independent unwind library
|
||||
Copyright 2011 Linaro Limited
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
|
||||
// Copyright (c) 2010, 2011 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.
|
||||
|
||||
|
||||
// Derived from libunwind, with extensive modifications.
|
||||
// This file is derived from the following files in
|
||||
// toolkit/crashreporter/google-breakpad:
|
||||
// src/common/arm_ex_to_module.h
|
||||
// src/common/memory_range.h
|
||||
// src/common/arm_ex_reader.h
|
||||
|
||||
#ifndef LulExidxExt_h
|
||||
#define LulExidxExt_h
|
||||
|
||||
#include "LulMainInt.h"
|
||||
|
||||
using lul::LExpr;
|
||||
using lul::RuleSet;
|
||||
using lul::SecMap;
|
||||
|
||||
namespace lul {
|
||||
|
||||
typedef enum extab_cmd {
|
||||
ARM_EXIDX_CMD_FINISH,
|
||||
ARM_EXIDX_CMD_SUB_FROM_VSP,
|
||||
ARM_EXIDX_CMD_ADD_TO_VSP,
|
||||
ARM_EXIDX_CMD_REG_POP,
|
||||
ARM_EXIDX_CMD_REG_TO_SP,
|
||||
ARM_EXIDX_CMD_VFP_POP,
|
||||
ARM_EXIDX_CMD_WREG_POP,
|
||||
ARM_EXIDX_CMD_WCGR_POP,
|
||||
ARM_EXIDX_CMD_RESERVED,
|
||||
ARM_EXIDX_CMD_REFUSED,
|
||||
} extab_cmd_t;
|
||||
|
||||
struct exidx_entry {
|
||||
uint32_t addr;
|
||||
uint32_t data;
|
||||
};
|
||||
|
||||
struct extab_data {
|
||||
extab_cmd_t cmd;
|
||||
uint32_t data;
|
||||
};
|
||||
|
||||
enum extab_cmd_flags {
|
||||
ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
|
||||
ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX
|
||||
};
|
||||
|
||||
// Receives information from arm_ex_reader::ExceptionTableInfo
|
||||
// and adds it to the SecMap object
|
||||
// This is in effect the EXIDX summariser.
|
||||
class ARMExToModule {
|
||||
public:
|
||||
ARMExToModule(SecMap* smap, void(*log)(const char*)) : smap_(smap)
|
||||
, log_(log) { }
|
||||
~ARMExToModule() { }
|
||||
void AddStackFrame(uintptr_t addr, size_t size);
|
||||
int ImproveStackFrame(const struct extab_data* edata);
|
||||
void DeleteStackFrame();
|
||||
void SubmitStackFrame();
|
||||
private:
|
||||
SecMap* smap_;
|
||||
LExpr vsp_; // Always appears to be of the form "sp + offset"
|
||||
RuleSet curr_rules_; // Also holds the address range being summarised
|
||||
// debugging message sink
|
||||
void (*log_)(const char*);
|
||||
int TranslateCmd(const struct extab_data* edata, LExpr& vsp);
|
||||
};
|
||||
|
||||
|
||||
// (derived from)
|
||||
// memory_range.h: Define the google_breakpad::MemoryRange class, which
|
||||
// is a lightweight wrapper with a pointer and a length to encapsulate
|
||||
// a contiguous range of memory.
|
||||
|
||||
// A lightweight wrapper with a pointer and a length to encapsulate a
|
||||
// contiguous range of memory. It provides helper methods for checked
|
||||
// access of a subrange of the memory. Its implemementation does not
|
||||
// allocate memory or call into libc functions, and is thus safer to use
|
||||
// in a crashed environment.
|
||||
class MemoryRange {
|
||||
public:
|
||||
|
||||
MemoryRange(const void* data, size_t length) {
|
||||
Set(data, length);
|
||||
}
|
||||
|
||||
// Sets this memory range to point to |data| and its length to |length|.
|
||||
void Set(const void* data, size_t length) {
|
||||
data_ = reinterpret_cast<const uint8_t*>(data);
|
||||
// Always set |length_| to zero if |data_| is NULL.
|
||||
length_ = data ? length : 0;
|
||||
}
|
||||
|
||||
// Returns true if this range covers a subrange of |sub_length| bytes
|
||||
// at |sub_offset| bytes of this memory range, or false otherwise.
|
||||
bool Covers(size_t sub_offset, size_t sub_length) const {
|
||||
// The following checks verify that:
|
||||
// 1. sub_offset is within [ 0 .. length_ - 1 ]
|
||||
// 2. sub_offset + sub_length is within
|
||||
// [ sub_offset .. length_ ]
|
||||
return sub_offset < length_ &&
|
||||
sub_offset + sub_length >= sub_offset &&
|
||||
sub_offset + sub_length <= length_;
|
||||
}
|
||||
|
||||
// Returns a pointer to the beginning of this memory range.
|
||||
const uint8_t* data() const { return data_; }
|
||||
|
||||
// Returns the length, in bytes, of this memory range.
|
||||
size_t length() const { return length_; }
|
||||
|
||||
private:
|
||||
// Pointer to the beginning of this memory range.
|
||||
const uint8_t* data_;
|
||||
|
||||
// Length, in bytes, of this memory range.
|
||||
size_t length_;
|
||||
};
|
||||
|
||||
|
||||
// This class is a reader for ARM unwind information
|
||||
// from .ARM.exidx and .ARM.extab sections.
|
||||
class ExceptionTableInfo {
|
||||
public:
|
||||
ExceptionTableInfo(const char* exidx, size_t exidx_size,
|
||||
const char* extab, size_t extab_size,
|
||||
uint32_t text_last_svma,
|
||||
lul::ARMExToModule* handler,
|
||||
const char* mapping_addr,
|
||||
uint32_t loading_addr,
|
||||
uintptr_t text_bias,
|
||||
void (*log)(const char*))
|
||||
: mr_exidx_(lul::MemoryRange(exidx, exidx_size)),
|
||||
mr_extab_(lul::MemoryRange(extab, extab_size)),
|
||||
text_last_svma_(text_last_svma),
|
||||
handler_(handler), mapping_addr_(mapping_addr),
|
||||
loading_addr_(loading_addr),
|
||||
text_bias_(text_bias),
|
||||
log_(log) { }
|
||||
|
||||
~ExceptionTableInfo() { }
|
||||
|
||||
// Parses the entries in .ARM.exidx and possibly
|
||||
// in .ARM.extab tables, reports what we find to
|
||||
// arm_ex_to_module::ARMExToModule.
|
||||
void Start();
|
||||
|
||||
private:
|
||||
lul::MemoryRange mr_exidx_;
|
||||
lul::MemoryRange mr_extab_;
|
||||
uint32_t text_last_svma_;
|
||||
lul::ARMExToModule* handler_;
|
||||
const char* mapping_addr_;
|
||||
uint32_t loading_addr_;
|
||||
uintptr_t text_bias_;
|
||||
// debugging message sink
|
||||
void (*log_)(const char*);
|
||||
enum ExExtractResult {
|
||||
ExSuccess, // success
|
||||
ExInBufOverflow, // out-of-range while reading .exidx
|
||||
ExOutBufOverflow, // output buffer is too small
|
||||
ExCantUnwind, // this function is marked CANT_UNWIND
|
||||
ExCantRepresent, // entry valid, but we can't represent it
|
||||
ExInvalid // entry is invalid
|
||||
};
|
||||
ExExtractResult
|
||||
ExtabEntryExtract(const struct lul::exidx_entry* entry,
|
||||
uint8_t* buf, size_t buf_size,
|
||||
/*OUT*/size_t* buf_used);
|
||||
|
||||
int ExtabEntryDecode(const uint8_t* buf, size_t buf_size);
|
||||
};
|
||||
|
||||
} // namespace lul
|
||||
|
||||
#endif // LulExidxExt_h
|
1878
tools/profiler/LulMain.cpp
Normal file
1878
tools/profiler/LulMain.cpp
Normal file
File diff suppressed because it is too large
Load Diff
311
tools/profiler/LulMain.h
Normal file
311
tools/profiler/LulMain.h
Normal file
@ -0,0 +1,311 @@
|
||||
/* -*- 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 <pthread.h> // pthread_t
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "LulPlatformMacros.h"
|
||||
#include "LulRWLock.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:
|
||||
// Construct a valid one.
|
||||
TaggedUWord(uintptr_t w)
|
||||
: mValue(w)
|
||||
, mValid(true)
|
||||
{}
|
||||
|
||||
// Construct an invalid one.
|
||||
TaggedUWord()
|
||||
: mValue(0)
|
||||
, mValid(false)
|
||||
{}
|
||||
|
||||
// Add in a second one.
|
||||
void Add(TaggedUWord other) {
|
||||
if (mValid && other.Valid()) {
|
||||
mValue += other.Value();
|
||||
} else {
|
||||
mValue = 0;
|
||||
mValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Is it word-aligned?
|
||||
bool IsAligned() const {
|
||||
return mValid && (mValue & (sizeof(uintptr_t)-1)) == 0;
|
||||
}
|
||||
|
||||
uintptr_t Value() const { return mValue; }
|
||||
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];
|
||||
};
|
||||
|
||||
|
||||
// The core unwinder library class. Just one of these is needed, and
|
||||
// it can be shared by multiple unwinder threads.
|
||||
//
|
||||
// Access to the library is mediated by a single reader-writer lock.
|
||||
// All attempts to change the library's internal shared state -- that
|
||||
// is, loading or unloading unwind info -- are forced single-threaded
|
||||
// by causing the called routine to acquire a write-lock. Unwind
|
||||
// requests do not change the library's internal shared state and
|
||||
// therefore require only a read-lock. Hence multiple threads can
|
||||
// unwind in parallel.
|
||||
//
|
||||
// The library needs to maintain state which is private to each
|
||||
// unwinder thread -- the CFI (Dwarf Call Frame Information) fast
|
||||
// cache. Hence unwinder threads first need to register with the
|
||||
// library, so their identities are known. Also, for maximum
|
||||
// effectiveness of the CFI caching, it is preferable to have a small
|
||||
// number of very-busy unwinder threads rather than a large number of
|
||||
// mostly-idle unwinder threads.
|
||||
//
|
||||
// None of the methods may be safely called from within a signal
|
||||
// handler, since this risks deadlock. In particular this means
|
||||
// a thread may not unwind itself from within a signal handler
|
||||
// frame. It might be safe to call Unwind() on its own stack
|
||||
// from not-inside a signal frame, although even that cannot be
|
||||
// guaranteed deadlock free.
|
||||
|
||||
class PriMap;
|
||||
class SegArray;
|
||||
class CFICache;
|
||||
|
||||
class LUL {
|
||||
public:
|
||||
// Create; supply a logging sink. Initialises the rw-lock.
|
||||
LUL(void (*aLog)(const char*));
|
||||
|
||||
// Destroy. This acquires mRWlock for writing. By doing that, waits
|
||||
// for all unwinder threads to finish any Unwind() calls they may be
|
||||
// in. All resources are freed and all registered unwinder threads
|
||||
// are deregistered.
|
||||
~LUL();
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Acquires mRWlock for writing. This must be called only after the
|
||||
// code area in question really has been mapped.
|
||||
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. Acquires mRWlock for
|
||||
// writing. This must be called only after the code area in
|
||||
// question really has been mapped.
|
||||
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.
|
||||
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.
|
||||
void NotifyBeforeUnmapAll() {
|
||||
NotifyBeforeUnmap(0, UINTPTR_MAX);
|
||||
}
|
||||
|
||||
// Returns the number of mappings currently registered. Acquires
|
||||
// mRWlock for writing.
|
||||
size_t CountMappings();
|
||||
|
||||
// Register the calling thread for unwinding. Acquires mRWlock for
|
||||
// writing.
|
||||
void RegisterUnwinderThread();
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Acquires mRWlock for reading. Hence multiple threads may unwind
|
||||
// at once, but no thread may be unwinding whilst the library loads
|
||||
// or discards unwind information. Returns false if the calling
|
||||
// thread is not registered for unwinding.
|
||||
//
|
||||
// Up to aScannedFramesAllowed stack-scanned frames may be recovered.
|
||||
//
|
||||
// The calling thread must previously have registered itself via
|
||||
// RegisterUnwinderThread.
|
||||
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.
|
||||
void (*mLog)(const char*);
|
||||
|
||||
private:
|
||||
// Invalidate the caches. Requires mRWlock to be held for writing;
|
||||
// does not acquire it itself.
|
||||
void InvalidateCFICaches();
|
||||
|
||||
// The one-and-only lock, a reader-writer lock, for the library.
|
||||
LulRWLock* mRWlock;
|
||||
|
||||
// The top level mapping from code address ranges to postprocessed
|
||||
// unwind info. Basically a sorted array of (addr, len, info)
|
||||
// records. Threads wishing to query this field must hold mRWlock
|
||||
// for reading. Threads wishing to modify this field must hold
|
||||
// mRWlock for writing. 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. Threads
|
||||
// wishing to query this field must hold mRWlock for reading.
|
||||
// Threads wishing to modify this field must hold mRWlock for
|
||||
// writing.
|
||||
SegArray* mSegArray;
|
||||
|
||||
// The thread-local data: a mapping from threads to CFI-fast-caches.
|
||||
// Threads wishing to query this field must hold mRWlock for
|
||||
// reading. Threads wishing to modify this field must hold mRWlock
|
||||
// for writing.
|
||||
//
|
||||
// The CFICaches themselves are thread-local and can be both read
|
||||
// and written when mRWlock is held for reading. It would probably
|
||||
// be faster to use the pthread_{set,get}specific functions, but
|
||||
// also more difficult. This map is queried once per unwind, in
|
||||
// order to get hold of the CFI cache for a given thread.
|
||||
std::map<pthread_t, CFICache*> mCaches;
|
||||
};
|
||||
|
||||
|
||||
// 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
|
266
tools/profiler/LulMainInt.h
Normal file
266
tools/profiler/LulMainInt.h
Normal file
@ -0,0 +1,266 @@
|
||||
/* -*- 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 LulMainInt_h
|
||||
#define LulMainInt_h
|
||||
|
||||
#include "LulPlatformMacros.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
// This file is provides internal interface inside LUL. If you are an
|
||||
// end-user of LUL, do not include it in your code. The end-user
|
||||
// interface is in LulMain.h.
|
||||
|
||||
|
||||
namespace lul {
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DW_REG_ constants //
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// These are the Dwarf CFI register numbers, as (presumably) defined
|
||||
// in the ELF ABI supplements for each architecture.
|
||||
|
||||
enum DW_REG_NUMBER {
|
||||
// No real register has this number. It's convenient to be able to
|
||||
// treat the CFA (Canonical Frame Address) as "just another
|
||||
// register", though.
|
||||
DW_REG_CFA = -1,
|
||||
#if defined(LUL_ARCH_arm)
|
||||
// ARM registers
|
||||
DW_REG_ARM_R7 = 7,
|
||||
DW_REG_ARM_R11 = 11,
|
||||
DW_REG_ARM_R12 = 12,
|
||||
DW_REG_ARM_R13 = 13,
|
||||
DW_REG_ARM_R14 = 14,
|
||||
DW_REG_ARM_R15 = 15,
|
||||
#elif defined(LUL_ARCH_x64)
|
||||
// Because the X86 (32 bit) and AMD64 (64 bit) summarisers are
|
||||
// combined, a merged set of register constants is needed.
|
||||
DW_REG_INTEL_XBP = 6,
|
||||
DW_REG_INTEL_XSP = 7,
|
||||
DW_REG_INTEL_XIP = 16,
|
||||
#elif defined(LUL_ARCH_x86)
|
||||
DW_REG_INTEL_XBP = 5,
|
||||
DW_REG_INTEL_XSP = 4,
|
||||
DW_REG_INTEL_XIP = 8,
|
||||
#else
|
||||
# error "Unknown arch"
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// LExpr //
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// An expression -- very primitive. Denotes either "register +
|
||||
// offset" or a dereferenced version of the same. So as to allow
|
||||
// convenient handling of Dwarf-derived unwind info, the register may
|
||||
// also denote the CFA. A large number of these need to be stored, so
|
||||
// we ensure it fits into 8 bytes. See comment below on RuleSet to
|
||||
// see how expressions fit into the bigger picture.
|
||||
|
||||
struct LExpr {
|
||||
// Denotes an expression with no value.
|
||||
LExpr()
|
||||
: mHow(UNKNOWN)
|
||||
, mReg(0)
|
||||
, mOffset(0)
|
||||
{}
|
||||
|
||||
// Denotes any expressible expression.
|
||||
LExpr(uint8_t how, int16_t reg, int32_t offset)
|
||||
: mHow(how)
|
||||
, mReg(reg)
|
||||
, mOffset(offset)
|
||||
{}
|
||||
|
||||
// Change the offset for an expression that references memory.
|
||||
LExpr add_delta(long delta)
|
||||
{
|
||||
MOZ_ASSERT(mHow == NODEREF);
|
||||
// If this is a non-debug build and the above assertion would have
|
||||
// failed, at least return LExpr() so that the machinery that uses
|
||||
// the resulting expression fails in a repeatable way.
|
||||
return (mHow == NODEREF) ? LExpr(mHow, mReg, mOffset+delta)
|
||||
: LExpr(); // Gone bad
|
||||
}
|
||||
|
||||
// Dereference an expression that denotes a memory address.
|
||||
LExpr deref()
|
||||
{
|
||||
MOZ_ASSERT(mHow == NODEREF);
|
||||
// Same rationale as for add_delta().
|
||||
return (mHow == NODEREF) ? LExpr(DEREF, mReg, mOffset)
|
||||
: LExpr(); // Gone bad
|
||||
}
|
||||
|
||||
// Representation of expressions. If |mReg| is DW_REG_CFA (-1) then
|
||||
// it denotes the CFA. All other allowed values for |mReg| are
|
||||
// nonnegative and are DW_REG_ values.
|
||||
|
||||
enum { UNKNOWN=0, // This LExpr denotes no value.
|
||||
NODEREF, // Value is (mReg + mOffset).
|
||||
DEREF }; // Value is *(mReg + mOffset).
|
||||
|
||||
uint8_t mHow; // UNKNOWN, NODEREF or DEREF
|
||||
int16_t mReg; // A DW_REG_ value
|
||||
int32_t mOffset; // 32-bit signed offset should be more than enough.
|
||||
};
|
||||
|
||||
static_assert(sizeof(LExpr) <= 8, "LExpr size changed unexpectedly");
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// RuleSet //
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// This is platform-dependent. For some address range, describes how
|
||||
// to recover the CFA and then how to recover the registers for the
|
||||
// previous frame.
|
||||
//
|
||||
// The set of LExprs contained in a given RuleSet describe a DAG which
|
||||
// says how to compute the caller's registers ("new registers") from
|
||||
// the callee's registers ("old registers"). The DAG can contain a
|
||||
// single internal node, which is the value of the CFA for the callee.
|
||||
// It would be possible to construct a DAG that omits the CFA, but
|
||||
// including it makes the summarisers simpler, and the Dwarf CFI spec
|
||||
// has the CFA as a central concept.
|
||||
//
|
||||
// For this to make sense, |mCfaExpr| can't have
|
||||
// |mReg| == DW_REG_CFA since we have no previous value for the CFA.
|
||||
// All of the other |Expr| fields can -- and usually do -- specify
|
||||
// |mReg| == DW_REG_CFA.
|
||||
//
|
||||
// With that in place, the unwind algorithm proceeds as follows.
|
||||
//
|
||||
// (0) Initially: we have values for the old registers, and a memory
|
||||
// image.
|
||||
//
|
||||
// (1) Compute the CFA by evaluating |mCfaExpr|. Add the computed
|
||||
// value to the set of "old registers".
|
||||
//
|
||||
// (2) Compute values for the registers by evaluating all of the other
|
||||
// |Expr| fields in the RuleSet. These can depend on both the old
|
||||
// register values and the just-computed CFA.
|
||||
//
|
||||
// If we are unwinding without computing a CFA, perhaps because the
|
||||
// RuleSets are derived from EXIDX instead of Dwarf, then
|
||||
// |mCfaExpr.mHow| will be LExpr::UNKNOWN, so the computed value will
|
||||
// be invalid -- that is, TaggedUWord() -- and so any attempt to use
|
||||
// that will result in the same value. But that's OK because the
|
||||
// RuleSet would make no sense if depended on the CFA but specified no
|
||||
// way to compute it.
|
||||
//
|
||||
// A RuleSet is not allowed to cover zero address range. Having zero
|
||||
// length would break binary searching in SecMaps and PriMaps.
|
||||
|
||||
class RuleSet {
|
||||
public:
|
||||
RuleSet();
|
||||
void Print(void(*aLog)(const char*));
|
||||
|
||||
// Find the LExpr* for a given DW_REG_ value in this class.
|
||||
LExpr* ExprForRegno(DW_REG_NUMBER aRegno);
|
||||
|
||||
uintptr_t mAddr;
|
||||
uintptr_t mLen;
|
||||
// How to compute the CFA.
|
||||
LExpr mCfaExpr;
|
||||
// How to compute caller register values. These may reference the
|
||||
// value defined by |mCfaExpr|.
|
||||
#if defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
|
||||
LExpr mXipExpr; // return address
|
||||
LExpr mXspExpr;
|
||||
LExpr mXbpExpr;
|
||||
#elif defined(LUL_ARCH_arm)
|
||||
LExpr mR15expr; // return address
|
||||
LExpr mR14expr;
|
||||
LExpr mR13expr;
|
||||
LExpr mR12expr;
|
||||
LExpr mR11expr;
|
||||
LExpr mR7expr;
|
||||
#else
|
||||
# error "Unknown arch"
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// SecMap //
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// A SecMap may have zero address range, temporarily, whilst RuleSets
|
||||
// are being added to it. But adding a zero-range SecMap to a PriMap
|
||||
// will make it impossible to maintain the total order of the PriMap
|
||||
// entries, and so that can't be allowed to happen.
|
||||
|
||||
class SecMap {
|
||||
public:
|
||||
// These summarise the contained mRuleSets, in that they give
|
||||
// exactly the lowest and highest addresses that any of the entries
|
||||
// in this SecMap cover. Hence invariants:
|
||||
//
|
||||
// mRuleSets is nonempty
|
||||
// <=> mSummaryMinAddr <= mSummaryMaxAddr
|
||||
// && mSummaryMinAddr == mRuleSets[0].mAddr
|
||||
// && mSummaryMaxAddr == mRuleSets[#rulesets-1].mAddr
|
||||
// + mRuleSets[#rulesets-1].mLen - 1;
|
||||
//
|
||||
// This requires that no RuleSet has zero length.
|
||||
//
|
||||
// mRuleSets is empty
|
||||
// <=> mSummaryMinAddr > mSummaryMaxAddr
|
||||
//
|
||||
// This doesn't constrain mSummaryMinAddr and mSummaryMaxAddr uniquely,
|
||||
// so let's use mSummaryMinAddr == 1 and mSummaryMaxAddr == 0 to denote
|
||||
// this case.
|
||||
|
||||
SecMap(void(*aLog)(const char*));
|
||||
~SecMap();
|
||||
|
||||
// Binary search mRuleSets to find one that brackets |ia|, or nullptr
|
||||
// if none is found. It's not allowable to do this until PrepareRuleSets
|
||||
// has been called first.
|
||||
RuleSet* FindRuleSet(uintptr_t ia);
|
||||
|
||||
// Add a RuleSet to the collection. The rule is copied in. Calling
|
||||
// this makes the map non-searchable.
|
||||
void AddRuleSet(RuleSet* rs);
|
||||
|
||||
// Prepare the map for searching. Also, remove any rules for code
|
||||
// address ranges which don't fall inside [start, +len). |len| may
|
||||
// not be zero.
|
||||
void PrepareRuleSets(uintptr_t start, size_t len);
|
||||
|
||||
bool IsEmpty();
|
||||
|
||||
size_t Size() { return mRuleSets.size(); }
|
||||
|
||||
// The min and max addresses of the addresses in the contained
|
||||
// RuleSets. See comment above for invariants.
|
||||
uintptr_t mSummaryMinAddr;
|
||||
uintptr_t mSummaryMaxAddr;
|
||||
|
||||
private:
|
||||
// False whilst adding entries; true once it is safe to call FindRuleSet.
|
||||
// Transition (false->true) is caused by calling PrepareRuleSets().
|
||||
bool mUsable;
|
||||
|
||||
// A vector of RuleSets, sorted, nonoverlapping (post Prepare()).
|
||||
std::vector<RuleSet> mRuleSets;
|
||||
|
||||
// A logging sink, for debugging.
|
||||
void (*mLog)(const char*);
|
||||
};
|
||||
|
||||
} // namespace lul
|
||||
|
||||
#endif // ndef LulMainInt_h
|
53
tools/profiler/LulPlatformMacros.h
Normal file
53
tools/profiler/LulPlatformMacros.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- 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 LulPlatformMacros_h
|
||||
#define LulPlatformMacros_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Define platform selection macros in a consistent way. The primary
|
||||
// factorisation is on (ARCH,OS) pairs ("PLATforms") but ARCH_ and OS_
|
||||
// macros are defined too, since they are sometimes convenient.
|
||||
|
||||
#undef LUL_PLAT_x64_linux
|
||||
#undef LUL_PLAT_x86_linux
|
||||
#undef LUL_PLAT_arm_android
|
||||
#undef LUL_PLAT_x86_android
|
||||
|
||||
#undef LUL_ARCH_arm
|
||||
#undef LUL_ARCH_x86
|
||||
#undef LUL_ARCH_x64
|
||||
|
||||
#undef LUL_OS_android
|
||||
#undef LUL_OS_linux
|
||||
|
||||
#if defined(__linux__) && defined(__x86_64__)
|
||||
# define LUL_PLAT_x64_linux 1
|
||||
# define LUL_ARCH_x64 1
|
||||
# define LUL_OS_linux 1
|
||||
|
||||
#elif defined(__linux__) && defined(__i386__) && !defined(__ANDROID__)
|
||||
# define LUL_PLAT_x86_linux 1
|
||||
# define LUL_ARCH_x86 1
|
||||
# define LUL_OS_linux 1
|
||||
|
||||
#elif defined(__ANDROID__) && defined(__arm__)
|
||||
# define LUL_PLAT_arm_android 1
|
||||
# define LUL_ARCH_arm 1
|
||||
# define LUL_OS_android 1
|
||||
|
||||
#elif defined(__ANDROID__) && defined(__i386__)
|
||||
# define LUL_PLAT_x86_android 1
|
||||
# define LUL_ARCH_x86 1
|
||||
# define LUL_OS_android 1
|
||||
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
#endif // LulPlatformMacros_h
|
93
tools/profiler/LulRWLock.cpp
Normal file
93
tools/profiler/LulRWLock.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/* -*- 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 "mozilla/Assertions.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
|
||||
#include "LulRWLock.h"
|
||||
|
||||
|
||||
namespace lul {
|
||||
|
||||
// An implementation for targets where libpthread does provide
|
||||
// pthread_rwlock_t. These are straight wrappers around the
|
||||
// equivalent pthread functions.
|
||||
|
||||
#if defined(LUL_OS_linux)
|
||||
|
||||
LulRWLock::LulRWLock() {
|
||||
mozilla::DebugOnly<int> r = pthread_rwlock_init(&mLock, nullptr);
|
||||
MOZ_ASSERT(!r);
|
||||
}
|
||||
|
||||
LulRWLock::~LulRWLock() {
|
||||
mozilla::DebugOnly<int>r = pthread_rwlock_destroy(&mLock);
|
||||
MOZ_ASSERT(!r);
|
||||
}
|
||||
|
||||
void
|
||||
LulRWLock::WrLock() {
|
||||
mozilla::DebugOnly<int>r = pthread_rwlock_wrlock(&mLock);
|
||||
MOZ_ASSERT(!r);
|
||||
}
|
||||
|
||||
void
|
||||
LulRWLock::RdLock() {
|
||||
mozilla::DebugOnly<int>r = pthread_rwlock_rdlock(&mLock);
|
||||
MOZ_ASSERT(!r);
|
||||
}
|
||||
|
||||
void
|
||||
LulRWLock::Unlock() {
|
||||
mozilla::DebugOnly<int>r = pthread_rwlock_unlock(&mLock);
|
||||
MOZ_ASSERT(!r);
|
||||
}
|
||||
|
||||
|
||||
// An implementation for cases where libpthread does not provide
|
||||
// pthread_rwlock_t. Currently this is a kludge in that it uses
|
||||
// normal mutexes, resulting in the following limitations: (1) at most
|
||||
// one reader is allowed at once, and (2) any thread that tries to
|
||||
// read-acquire the lock more than once will deadlock. (2) could be
|
||||
// avoided if it were possible to use recursive pthread_mutex_t's.
|
||||
|
||||
#elif defined(LUL_OS_android)
|
||||
|
||||
LulRWLock::LulRWLock() {
|
||||
mozilla::DebugOnly<int> r = pthread_mutex_init(&mLock, nullptr);
|
||||
MOZ_ASSERT(!r);
|
||||
}
|
||||
|
||||
LulRWLock::~LulRWLock() {
|
||||
mozilla::DebugOnly<int>r = pthread_mutex_destroy(&mLock);
|
||||
MOZ_ASSERT(!r);
|
||||
}
|
||||
|
||||
void
|
||||
LulRWLock::WrLock() {
|
||||
mozilla::DebugOnly<int>r = pthread_mutex_lock(&mLock);
|
||||
MOZ_ASSERT(!r);
|
||||
}
|
||||
|
||||
void
|
||||
LulRWLock::RdLock() {
|
||||
mozilla::DebugOnly<int>r = pthread_mutex_lock(&mLock);
|
||||
MOZ_ASSERT(!r);
|
||||
}
|
||||
|
||||
void
|
||||
LulRWLock::Unlock() {
|
||||
mozilla::DebugOnly<int>r = pthread_mutex_unlock(&mLock);
|
||||
MOZ_ASSERT(!r);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
# error "Unsupported OS"
|
||||
#endif
|
||||
|
||||
} // namespace lul
|
42
tools/profiler/LulRWLock.h
Normal file
42
tools/profiler/LulRWLock.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* -*- 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 LulRWLock_h
|
||||
#define LulRWLock_h
|
||||
|
||||
#include "LulPlatformMacros.h"
|
||||
#include <pthread.h>
|
||||
|
||||
// This class provides a simple wrapper around reader-writer locks.
|
||||
// This is necessary because Android 2.2's libpthread does not
|
||||
// provide such functionality, so there has to be a way to provide
|
||||
// an alternative with an equivalent interface.
|
||||
|
||||
namespace lul {
|
||||
|
||||
class LulRWLock {
|
||||
|
||||
public:
|
||||
LulRWLock();
|
||||
~LulRWLock();
|
||||
void WrLock();
|
||||
void RdLock();
|
||||
void Unlock();
|
||||
|
||||
private:
|
||||
#if defined(LUL_OS_android)
|
||||
pthread_mutex_t mLock;
|
||||
#elif defined(LUL_OS_linux)
|
||||
pthread_rwlock_t mLock;
|
||||
#else
|
||||
# error "Unsupported OS"
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
} // namespace lul
|
||||
|
||||
#endif // LulRWLock_h
|
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,11 @@
|
||||
#include "GeckoProfilerImpl.h"
|
||||
#include "ProfileEntry.h"
|
||||
|
||||
#include "PlatformMacros.h"
|
||||
#if defined(SPS_OS_android) || defined(SPS_OS_linux)
|
||||
# include "LulMain.h"
|
||||
#endif
|
||||
|
||||
/* Top level exports of UnwinderThread.cpp. */
|
||||
|
||||
// Abstract type. A buffer which is used to transfer information between
|
||||
@ -89,4 +94,11 @@ void utb__release_sync_buffer(LinkedUWTBuffer* utb);
|
||||
// This typedef must match uwt__release_full_buffer and uwt__finish_sync_buffer
|
||||
typedef void (*UTB_RELEASE_FUNC)(ThreadProfile*,UnwinderThreadBuffer*,void*);
|
||||
|
||||
#if defined(SPS_OS_android) || defined(SPS_OS_linux)
|
||||
// Notify |aLUL| of the objects in the current process, so as to get
|
||||
// it to read unwind data for them. This has to be externally visible
|
||||
// so it can be used in LUL unit tests, tools/profiler/tests/gtest/LulTest.cpp.
|
||||
void read_procmaps(lul::LUL* aLUL);
|
||||
#endif
|
||||
|
||||
#endif /* ndef MOZ_UNWINDER_THREAD_2_H */
|
||||
|
@ -45,12 +45,24 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
|
||||
# These files cannot be built in unified mode because of name clashes with mozglue headers on Android.
|
||||
UNIFIED_SOURCES += [
|
||||
'AutoObjectMapper.cpp',
|
||||
'LulCommon.cpp',
|
||||
'LulDwarf.cpp',
|
||||
'LulDwarfSummariser.cpp',
|
||||
'LulElf.cpp',
|
||||
'LulMain.cpp',
|
||||
'LulRWLock.cpp',
|
||||
]
|
||||
# These files cannot be built in unified mode because of name clashes with mozglue headers on Android.
|
||||
SOURCES += [
|
||||
'platform-linux.cc',
|
||||
'shared-libraries-linux.cc',
|
||||
]
|
||||
if CONFIG['CPU_ARCH'] == 'arm':
|
||||
UNIFIED_SOURCES += [
|
||||
'LulExidx.cpp',
|
||||
]
|
||||
SOURCES += [
|
||||
'EHABIStackWalk.cpp',
|
||||
]
|
||||
@ -82,6 +94,9 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
|
||||
if CONFIG['ANDROID_CPU_ARCH'] == 'armeabi':
|
||||
DEFINES['ARCH_ARMV6'] = True
|
||||
|
||||
if CONFIG['ENABLE_TESTS']:
|
||||
DIRS += ['tests/gtest']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
EXPORTS += [
|
||||
|
@ -237,7 +237,6 @@ bool sps_version2()
|
||||
return version == 2;
|
||||
}
|
||||
|
||||
#if !defined(ANDROID)
|
||||
/* Has MOZ_PROFILER_VERBOSE been set? */
|
||||
bool moz_profiler_verbose()
|
||||
{
|
||||
@ -253,7 +252,6 @@ bool moz_profiler_verbose()
|
||||
|
||||
return status == 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline const char* name_UnwMode(UnwMode m)
|
||||
{
|
||||
@ -367,6 +365,7 @@ void read_profiler_env_vars()
|
||||
LOGF("SPS: UnwindStackScan = %d (max dubious frames per unwind).",
|
||||
(int)sUnwindStackScan);
|
||||
LOG( "SPS: Use env var MOZ_PROFILER_MODE=help for further information.");
|
||||
LOG( "SPS: Note that MOZ_PROFILER_MODE=help sets all values to defaults.");
|
||||
LOG( "SPS:");
|
||||
}
|
||||
}
|
||||
@ -394,7 +393,11 @@ void profiler_usage() {
|
||||
LOG( "SPS: The number of dubious (stack-scanned) frames allowed");
|
||||
LOG( "SPS: ");
|
||||
LOG( "SPS: MOZ_PROFILER_NEW");
|
||||
LOG( "SPS: Needs to be set to use Breakpad-based unwinding.");
|
||||
LOG( "SPS: Needs to be set to use LUL-based unwinding.");
|
||||
LOG( "SPS: ");
|
||||
LOG( "SPS: MOZ_PROFILER_LUL_TEST");
|
||||
LOG( "SPS: If set to any value, runs LUL unit tests at startup of");
|
||||
LOG( "SPS: the unwinder thread, and prints a short summary of results.");
|
||||
LOG( "SPS: ");
|
||||
LOGF("SPS: This platform %s native unwinding.",
|
||||
is_native_unwinding_avail() ? "supports" : "does not support");
|
||||
@ -415,6 +418,7 @@ void profiler_usage() {
|
||||
LOGF("SPS: UnwindStackScan = %d (max dubious frames per unwind).",
|
||||
(int)sUnwindStackScan);
|
||||
LOG( "SPS: Use env var MOZ_PROFILER_MODE=help for further information.");
|
||||
LOG( "SPS: Note that MOZ_PROFILER_MODE=help sets all values to defaults.");
|
||||
LOG( "SPS:");
|
||||
|
||||
return;
|
||||
@ -614,6 +618,8 @@ void mozilla_sampler_start(int aProfileEntries, double aInterval,
|
||||
const char** aThreadNameFilters, uint32_t aFilterCount)
|
||||
|
||||
{
|
||||
LOG("BEGIN mozilla_sampler_start");
|
||||
|
||||
if (!stack_key_initialized)
|
||||
profiler_init(nullptr);
|
||||
|
||||
@ -686,15 +692,20 @@ void mozilla_sampler_start(int aProfileEntries, double aInterval,
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os)
|
||||
os->NotifyObservers(nullptr, "profiler-started", nullptr);
|
||||
|
||||
LOG("END mozilla_sampler_start");
|
||||
}
|
||||
|
||||
void mozilla_sampler_stop()
|
||||
{
|
||||
LOG("BEGIN mozilla_sampler_stop");
|
||||
|
||||
if (!stack_key_initialized)
|
||||
profiler_init(nullptr);
|
||||
|
||||
TableTicker *t = tlsTicker.get();
|
||||
if (!t) {
|
||||
LOG("END mozilla_sampler_stop-early");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -732,6 +743,8 @@ void mozilla_sampler_stop()
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os)
|
||||
os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
|
||||
|
||||
LOG("END mozilla_sampler_stop");
|
||||
}
|
||||
|
||||
bool mozilla_sampler_is_paused() {
|
||||
|
@ -54,18 +54,24 @@
|
||||
|
||||
#define ASSERT(a) MOZ_ASSERT(a)
|
||||
|
||||
bool moz_profiler_verbose();
|
||||
|
||||
#ifdef ANDROID
|
||||
# if defined(__arm__) || defined(__thumb__)
|
||||
# define ENABLE_SPS_LEAF_DATA
|
||||
# define ENABLE_ARM_LR_SAVING
|
||||
# endif
|
||||
# define LOG(text) \
|
||||
__android_log_write(ANDROID_LOG_ERROR, "Profiler", text)
|
||||
do { if (moz_profiler_verbose()) \
|
||||
__android_log_write(ANDROID_LOG_ERROR, "Profiler", text); \
|
||||
} while (0)
|
||||
# define LOGF(format, ...) \
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Profiler", format, __VA_ARGS__)
|
||||
do { if (moz_profiler_verbose()) \
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Profiler", format, \
|
||||
__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
extern bool moz_profiler_verbose();
|
||||
# define LOG(text) \
|
||||
do { if (moz_profiler_verbose()) fprintf(stderr, "Profiler: %s\n", text); \
|
||||
} while (0)
|
||||
|
50
tools/profiler/tests/gtest/LulTest.cpp
Normal file
50
tools/profiler/tests/gtest/LulTest.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- 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 "gtest/gtest.h"
|
||||
#include "LulMain.h"
|
||||
#include "GeckoProfiler.h" // for TracingMetadata
|
||||
#include "UnwinderThread2.h" // for read_procmaps
|
||||
|
||||
// Set this to 0 to make LUL be completely silent during tests.
|
||||
// Set it to 1 to get logging output from LUL, presumably for
|
||||
// the purpose of debugging it.
|
||||
#define DEBUG_LUL_TEST 0
|
||||
|
||||
// LUL needs a callback for its logging sink.
|
||||
static void
|
||||
logging_sink_for_LUL(const char* str) {
|
||||
if (DEBUG_LUL_TEST == 0) {
|
||||
return;
|
||||
}
|
||||
// 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;
|
||||
fprintf(stderr, "LUL-in-gtest: %s\n", tmp);
|
||||
free(tmp);
|
||||
} else {
|
||||
fprintf(stderr, "LUL-in-gtest: %s\n", str);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(LUL, unwind_consistency) {
|
||||
// Set up LUL and get it to read unwind info for libxul.so, which is
|
||||
// all we care about here, plus (incidentally) practically every
|
||||
// other object in the process too.
|
||||
lul::LUL* lul = new lul::LUL(logging_sink_for_LUL);
|
||||
lul->RegisterUnwinderThread();
|
||||
read_procmaps(lul);
|
||||
|
||||
// Run unwind tests and receive information about how many there
|
||||
// were and how many were successful.
|
||||
int nTests = 0, nTestsPassed = 0;
|
||||
RunLulUnitTests(&nTests, &nTestsPassed, lul);
|
||||
EXPECT_TRUE(nTests == 6) << "Unexpected number of tests";
|
||||
EXPECT_TRUE(nTestsPassed == nTests) << "Not all tests passed";
|
||||
|
||||
delete lul;
|
||||
}
|
20
tools/profiler/tests/gtest/moz.build
Normal file
20
tools/profiler/tests/gtest/moz.build
Normal file
@ -0,0 +1,20 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
LIBRARY_NAME = 'sps_test'
|
||||
|
||||
if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
|
||||
UNIFIED_SOURCES += [
|
||||
'LulTest.cpp',
|
||||
]
|
||||
|
||||
EXPORT_LIBRARY = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/tools/profiler',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
Loading…
Reference in New Issue
Block a user