adda9a5022
More changes for HPHP to help make it clang friendly ~~~hphp/compiler/expression/constant_expression.h ~~~hphp/compiler/expression/function_call.h rfind returns a size_t/unsigned int ~~~hphp/runtime/base/server/http_protocol.cpp Switched to std::to_string. Assuming [] was not intended here ~~~hphp/runtime/base/ref_data.h These fields were accessed in a public manner, assuming public was intended instead of private ~~~hphp/runtime/base/variable_serializer.cpp Switched to using [] and & to make clang happy. Assuming this was to either take or drop the first char. ~~~hphp/runtime/ext/asio/asio_external_thread_event_queue.h ~~~hphp/runtime/ext/asio/asio_external_thread_event_queue.cpp Cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression. This is been moved to a macro as a temporary fix. +++hphp/runtime/ext/ext_misc.cpp Added std::atomic to supress warnings ~~~hphp/runtime/vm/jit/simplifier.cpp Chosen constructor is explicit in copy-initialization ~~~hphp/runtime/vm/jit/translator-asm-helpers.S Ambiguous instructions require an explicit suffix Changed cmp to cmpl ~~~hphp/runtime/vm/jit/translator-x64-helpers.cpp Clang does not support global register variables +++hphp/runtime/vm/unwind.cpp describeFault was only used when DEBUG or USE_TRACE was defined ~~~hphp/runtime/vm/verifier/check_unit.cpp Made fmt pointer const to avoid string format issues/warnings ~~~hphp/util/stack_trace.cpp Clang does not support variable-length arrays. Uniqe_ptr is used instead to take advantage runtime-sized arrays, a restriced form of variable-length arrays ~~~hphp/util/thread_local.h Clang seems to be supporting the __thread attribute, or at the very least it is not complaining about it. ~~~hphp/util/tiny_vector.h Clang does not like the flexible array here, since T is not always POD. I have reimplemented the array here by just sticking one value in the struct and calculating the offset from its address manually. Alterinatively, we could change the the non-POD types to be pointers, or we could edit their implemenations. +++hphp/util/util.h Created a template for the union, A function declared with the constexpr specifier cannot contain type declarations that do not define classes or enumerations +++hphp/runtime/vm/jit/x64-util.h Added a TODO The way hphp/runtime/vm/jit/x64-util.h is currently implemented, it only works if USE_GCC_FAST_TLS is defined
555 linhas
16 KiB
C++
555 linhas
16 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "hphp/util/stack_trace.h"
|
|
#include "hphp/util/process.h"
|
|
#include "hphp/util/base.h"
|
|
#include "hphp/util/lock.h"
|
|
#include "hphp/util/logger.h"
|
|
#include "util.h"
|
|
|
|
#include <execinfo.h>
|
|
#include <bfd.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include "hphp/util/light_process.h"
|
|
#include "hphp/util/compatibility.h"
|
|
#include "hphp/util/hash.h"
|
|
|
|
namespace HPHP {
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
std::string StackTrace::Frame::toString() const {
|
|
string out;
|
|
out = funcname.empty() ? "??" : funcname;
|
|
out += " at ";
|
|
out += filename.empty() ? "??" : filename;
|
|
out += ":";
|
|
out += lexical_cast<string>(lineno);
|
|
return out;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// signal handler
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Types
|
|
struct bfd_cache {
|
|
bfd_cache() : abfd(nullptr) {}
|
|
bfd *abfd;
|
|
asymbol **syms;
|
|
|
|
~bfd_cache() {
|
|
if (abfd) {
|
|
bfd_cache_close(abfd);
|
|
bfd_free_cached_info(abfd);
|
|
bfd_close_all_done(abfd);
|
|
}
|
|
}
|
|
};
|
|
|
|
static const int MaxKey = 100;
|
|
struct NamedBfd {
|
|
bfd_cache bc;
|
|
char key [MaxKey] ;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// statics
|
|
|
|
bool StackTraceBase::Enabled = true;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// constructor and destructor
|
|
|
|
StackTraceBase::StackTraceBase() {
|
|
bfd_init();
|
|
}
|
|
|
|
StackTrace::StackTrace(const StackTrace &bt) {
|
|
assert(this != &bt);
|
|
|
|
m_bt_pointers = bt.m_bt_pointers;
|
|
m_bt = bt.m_bt;
|
|
}
|
|
|
|
StackTrace::StackTrace(bool trace) {
|
|
if (trace && Enabled) {
|
|
create();
|
|
}
|
|
}
|
|
|
|
StackTraceNoHeap::StackTraceNoHeap(bool trace) {
|
|
if (trace && Enabled) {
|
|
create();
|
|
}
|
|
}
|
|
|
|
void StackTrace::initFromHex(const char *hexEncoded) {
|
|
vector<string> frames;
|
|
Util::split(':', hexEncoded, frames);
|
|
for (unsigned int i = 0; i < frames.size(); i++) {
|
|
m_bt_pointers.push_back((void*)strtoll(frames[i].c_str(), nullptr, 16));
|
|
}
|
|
}
|
|
|
|
StackTrace::StackTrace(const std::string &hexEncoded) {
|
|
initFromHex(hexEncoded.c_str());
|
|
}
|
|
|
|
StackTrace::StackTrace(const char *hexEncoded) {
|
|
initFromHex(hexEncoded);
|
|
}
|
|
|
|
void StackTrace::create() {
|
|
void *btpointers[MAXFRAME];
|
|
int framecount = 0;
|
|
framecount = backtrace(btpointers, MAXFRAME);
|
|
if (framecount <= 0 || framecount > (signed) MAXFRAME) {
|
|
m_bt_pointers.clear();
|
|
return;
|
|
}
|
|
m_bt_pointers.resize(framecount);
|
|
for (int i = 0; i < framecount; i++) {
|
|
m_bt_pointers[i] = btpointers[i];
|
|
}
|
|
}
|
|
|
|
void StackTraceNoHeap::create() {
|
|
int unsigned framecount = 0;
|
|
framecount = backtrace(m_btpointers, MAXFRAME);
|
|
if (framecount <= 0 || framecount > MAXFRAME) {
|
|
m_btpointers_cnt = 0;
|
|
return;
|
|
}
|
|
m_btpointers_cnt = framecount;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// reporting functions
|
|
|
|
const std::string &StackTrace::toString(int skip, int limit) const {
|
|
if (skip != 0 || limit != -1) m_bt.clear();
|
|
if (m_bt.empty()) {
|
|
size_t frame = 0;
|
|
for (vector<void*>::const_iterator btpi = m_bt_pointers.begin();
|
|
btpi != m_bt_pointers.end(); ++btpi) {
|
|
string framename = Translate(*btpi)->toString();
|
|
if (framename.find("StackTrace::") != string::npos) {
|
|
continue; // ignore frames in the StackTrace class
|
|
}
|
|
if (skip-- > 0) continue;
|
|
m_bt += "# ";
|
|
m_bt += lexical_cast<string>(frame);
|
|
if (frame < 10) m_bt += " ";
|
|
|
|
m_bt += " ";
|
|
m_bt += framename;
|
|
m_bt += "\n";
|
|
++frame;
|
|
if ((int)frame == limit) break;
|
|
}
|
|
}
|
|
return m_bt;
|
|
}
|
|
|
|
void StackTraceNoHeap::printStackTrace(int fd) const {
|
|
|
|
int frame = 0;
|
|
// m_btpointers_cnt must be an upper bound on the number of filenames
|
|
// then *2 for tolerable hash table behavior
|
|
unsigned int bfds_size = m_btpointers_cnt * 2;
|
|
const std::unique_ptr<NamedBfd[]> bfds (new NamedBfd[bfds_size]);
|
|
for (unsigned int i = 0; i < bfds_size; i++) bfds.get()[i].key[0]='\0';
|
|
for (unsigned int i = 0; i < m_btpointers_cnt; i++) {
|
|
if (Translate(fd, m_btpointers[i], frame, bfds.get(), bfds_size)) {
|
|
frame++;
|
|
}
|
|
}
|
|
// ~bfds[i].bc here (unlike the heap case)
|
|
}
|
|
|
|
void StackTrace::get(FramePtrVec &frames) const {
|
|
frames.clear();
|
|
for (vector<void*>::const_iterator btpi = m_bt_pointers.begin();
|
|
btpi != m_bt_pointers.end(); ++btpi) {
|
|
frames.push_back(Translate(*btpi));
|
|
}
|
|
}
|
|
|
|
std::string StackTrace::hexEncode(int minLevel /* = 0 */,
|
|
int maxLevel /* = 999 */) const {
|
|
string bts;
|
|
for (int i = minLevel; i < (int)m_bt_pointers.size() && i < maxLevel; i++) {
|
|
if (i > minLevel) bts += ':';
|
|
char buf[20];
|
|
snprintf(buf, sizeof(buf), "%" PRIx64, (int64_t)m_bt_pointers[i]);
|
|
bts.append(buf);
|
|
}
|
|
return bts;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// crash log
|
|
|
|
class StackTraceLog {
|
|
public:
|
|
hphp_string_map<std::string> data;
|
|
|
|
static DECLARE_THREAD_LOCAL(StackTraceLog, s_logData);
|
|
};
|
|
IMPLEMENT_THREAD_LOCAL(StackTraceLog, StackTraceLog::s_logData);
|
|
|
|
void StackTraceNoHeap::AddExtraLogging(const char *name,
|
|
const std::string &value) {
|
|
assert(name && *name);
|
|
StackTraceLog::s_logData->data[name] = value;
|
|
}
|
|
|
|
void StackTraceNoHeap::ClearAllExtraLogging() {
|
|
StackTraceLog::s_logData->data.clear();
|
|
}
|
|
|
|
void StackTraceNoHeap::log(const char *errorType, const char *tracefn,
|
|
const char *pid, const char *buildId,
|
|
int debuggerCount) const {
|
|
int fd = ::open(tracefn, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR);
|
|
if (fd < 0) return;
|
|
|
|
dprintf(fd, "Host: %s\n",Process::GetHostName().c_str());
|
|
dprintf(fd, "ProcessID: %s\n", pid);
|
|
dprintf(fd, "ThreadID: %" PRIx64"\n", (int64_t)Process::GetThreadId());
|
|
dprintf(fd, "ThreadPID: %u\n", Process::GetThreadPid());
|
|
dprintf(fd, "Name: %s\n", Process::GetAppName().c_str());
|
|
dprintf(fd, "Type: %s\n", errorType ? errorType : "(unknown error)");
|
|
dprintf(fd, "Runtime: hhvm\n");
|
|
dprintf(fd, "Version: %s\n", buildId);
|
|
dprintf(fd, "DebuggerCount: %d\n", debuggerCount);
|
|
dprintf(fd, "\n");
|
|
|
|
for (auto const& pair : StackTraceLog::s_logData->data) {
|
|
dprintf(fd, "%s: %s\n", pair.first.c_str(), pair.second.c_str());
|
|
}
|
|
dprintf(fd, "\n");
|
|
|
|
printStackTrace(fd);
|
|
|
|
::close(fd);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// helpers
|
|
|
|
struct addr2line_data {
|
|
asymbol **syms;
|
|
bfd_vma pc;
|
|
const char *filename;
|
|
const char *functionname;
|
|
unsigned int line;
|
|
bfd_boolean found;
|
|
};
|
|
|
|
|
|
bool StackTraceBase::Translate(void *frame, StackTraceBase::Frame * f,
|
|
Dl_info &dlInfo, void* data,
|
|
void *bfds, unsigned bfds_size) {
|
|
char sframe[32];
|
|
snprintf(sframe, sizeof(sframe), "%p", frame);
|
|
|
|
if (!dladdr(frame, &dlInfo)) {
|
|
return false;
|
|
}
|
|
|
|
// frame pointer offset in previous frame
|
|
f->offset = (char*)frame - (char*)dlInfo.dli_saddr;
|
|
|
|
if (dlInfo.dli_fname) {
|
|
|
|
// 1st attempt without offsetting base address
|
|
if (!Addr2line(dlInfo.dli_fname, sframe, f, data, bfds, bfds_size) &&
|
|
dlInfo.dli_fname && strstr(dlInfo.dli_fname,".so")) {
|
|
// offset shared lib's base address
|
|
frame = (char*)frame - (size_t)dlInfo.dli_fbase;
|
|
snprintf(sframe, sizeof(sframe), "%p", frame);
|
|
|
|
// Use addr2line to get line number info.
|
|
Addr2line(dlInfo.dli_fname, sframe, f, data, bfds, bfds_size);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
StackTrace::FramePtr StackTrace::Translate(void *frame) {
|
|
Dl_info dlInfo;
|
|
addr2line_data adata;
|
|
|
|
Frame * f1 = new Frame(frame);
|
|
FramePtr f(f1);
|
|
if (!StackTraceBase::Translate(frame, f1, dlInfo, &adata)) return f;
|
|
|
|
if (adata.filename) {
|
|
f->filename = adata.filename;
|
|
}
|
|
if (adata.functionname) {
|
|
f->funcname = Demangle(adata.functionname);
|
|
}
|
|
if (f->filename.empty() && dlInfo.dli_fname) {
|
|
f->filename = dlInfo.dli_fname;
|
|
}
|
|
if (f->funcname.empty() && dlInfo.dli_sname) {
|
|
f->funcname = Demangle(dlInfo.dli_sname);
|
|
}
|
|
|
|
return f;
|
|
}
|
|
|
|
bool StackTraceNoHeap::Translate(int fd, void *frame, int frame_num,
|
|
void *bfds, unsigned bfds_size) {
|
|
// frame pointer offset in previous frame
|
|
Dl_info dlInfo;
|
|
addr2line_data adata;
|
|
Frame f(frame);
|
|
if (!StackTraceBase::Translate(frame, &f, dlInfo, &adata, bfds,
|
|
bfds_size)) {
|
|
return false;
|
|
}
|
|
|
|
const char *filename = adata.filename ? adata.filename : dlInfo.dli_fname;
|
|
if (!filename) filename = "??";
|
|
const char *funcname = adata.functionname ? adata.functionname
|
|
: dlInfo.dli_sname;
|
|
if (!funcname) funcname = "??";
|
|
|
|
// ignore frames in the StackTrace class
|
|
if (strstr(funcname, "StackTraceNoHeap")) return false ;
|
|
|
|
dprintf(fd, "# %d%s ", frame_num, frame_num < 10 ? " " : "");
|
|
Demangle(fd, funcname);
|
|
dprintf(fd, " at %s:%u\n", filename, f.lineno);
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// copied and re-factored from addr2line
|
|
|
|
static void find_address_in_section(bfd *abfd, asection *section, void *data) {
|
|
addr2line_data *adata = reinterpret_cast<addr2line_data*>(data);
|
|
|
|
bfd_vma vma;
|
|
bfd_size_type size;
|
|
|
|
if (adata->found) {
|
|
return;
|
|
}
|
|
|
|
if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) {
|
|
return;
|
|
}
|
|
|
|
vma = bfd_get_section_vma(abfd, section);
|
|
if (adata->pc < vma) {
|
|
return;
|
|
}
|
|
|
|
size = bfd_get_section_size(section);
|
|
if (adata->pc >= vma + size) {
|
|
return;
|
|
}
|
|
|
|
adata->found = bfd_find_nearest_line(abfd, section, adata->syms,
|
|
adata->pc - vma, &adata->filename,
|
|
&adata->functionname, &adata->line);
|
|
if (adata->found) {
|
|
const char *file = adata->filename;
|
|
unsigned int line = adata->line;
|
|
bfd_boolean found = TRUE;
|
|
while (found) {
|
|
found = bfd_find_inliner_info(abfd, &file, &adata->functionname, &line);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool slurp_symtab(asymbol ***syms, bfd *abfd) {
|
|
long symcount;
|
|
unsigned int size;
|
|
|
|
symcount = bfd_read_minisymbols(abfd, FALSE, (void **)syms, &size);
|
|
if (symcount == 0) {
|
|
symcount = bfd_read_minisymbols(abfd, TRUE /* dynamic */, (void **)syms,
|
|
&size);
|
|
}
|
|
return symcount >= 0;
|
|
}
|
|
|
|
static bool translate_addresses(bfd *abfd, const char *addr,
|
|
addr2line_data *adata) {
|
|
if (!abfd) return false;
|
|
adata->pc = bfd_scan_vma(addr, nullptr, 16);
|
|
|
|
adata->found = FALSE;
|
|
bfd_map_over_sections(abfd, find_address_in_section, adata);
|
|
|
|
if (!adata->found || !adata->functionname || !*adata->functionname) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// We cache opened bfd file pointers that in turn cached frame pointer lookup
|
|
// tables.
|
|
|
|
|
|
typedef boost::shared_ptr<bfd_cache> bfd_cache_ptr;
|
|
typedef hphp_hash_map<std::string, bfd_cache_ptr, string_hash> bfdMap;
|
|
static Mutex s_bfdMutex;
|
|
static bfdMap s_bfds;
|
|
|
|
static bool fill_bfd_cache(const char *filename, bfd_cache *p) {
|
|
bfd *abfd = bfd_openr(filename, nullptr); // hard to avoid heap here!
|
|
if (!abfd) return true;
|
|
p->abfd = abfd;
|
|
p->syms = nullptr;
|
|
char **match;
|
|
if (bfd_check_format(abfd, bfd_archive) ||
|
|
!bfd_check_format_matches(abfd, bfd_object, &match) ||
|
|
!slurp_symtab(&p->syms, abfd)) {
|
|
bfd_close(abfd);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bfd_cache_ptr get_bfd_cache(const char *filename) {
|
|
bfdMap::const_iterator iter = s_bfds.find(filename);
|
|
if (iter != s_bfds.end()) {
|
|
return iter->second;
|
|
}
|
|
bfd_cache_ptr p(new bfd_cache());
|
|
if (fill_bfd_cache(filename, p.get())) {
|
|
p.reset();
|
|
}
|
|
s_bfds[filename] = p;
|
|
return p;
|
|
}
|
|
|
|
static bfd_cache * get_bfd_cache(const char *filename, NamedBfd* bfds,
|
|
int bfds_size) {
|
|
int probe = hash_string(filename) % bfds_size;
|
|
// match on the end of filename instead of the beginning, if necessary
|
|
int tooLong = strlen(filename) - MaxKey;
|
|
if (tooLong > 0) filename += tooLong;
|
|
while (bfds[probe].key[0]
|
|
&& strncmp(filename, bfds[probe].key, MaxKey) != 0) {
|
|
probe = probe ? probe-1 : bfds_size-1;
|
|
}
|
|
bfd_cache *p = &bfds[probe].bc;
|
|
if (bfds[probe].key[0]) return p;
|
|
// accept the rare collision on keys (requires probe collision too)
|
|
strncpy(bfds[probe].key, filename, MaxKey);
|
|
fill_bfd_cache(filename, p);
|
|
return p;
|
|
}
|
|
|
|
bool StackTraceBase::Addr2line(const char *filename, const char *address,
|
|
Frame *frame, void *adata,
|
|
void *bfds, unsigned bfds_size) {
|
|
Lock lock(s_bfdMutex);
|
|
addr2line_data *data = reinterpret_cast<addr2line_data*>(adata);
|
|
data->filename = nullptr;
|
|
data->functionname = nullptr;
|
|
data->line = 0;
|
|
bool ret;
|
|
|
|
if (!bfds) {
|
|
bfd_cache_ptr p = get_bfd_cache(filename);
|
|
if (!p) return false;
|
|
data->syms = p->syms;
|
|
ret = translate_addresses(p->abfd, address, data);
|
|
} else {
|
|
// don't let bfd_cache_ptr malloc behind the scenes in this case
|
|
bfd_cache *q = get_bfd_cache(filename, (NamedBfd*)bfds, bfds_size);
|
|
if (!q) return false;
|
|
data->syms = q->syms;
|
|
ret = translate_addresses(q->abfd, address, data);
|
|
}
|
|
|
|
if (ret) {
|
|
frame->lineno = data->line;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// copied and re-factored from demangle/c++filt
|
|
|
|
#define DMGL_PARAMS (1 << 0) /* Include function args */
|
|
#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
|
|
#define DMGL_VERBOSE (1 << 3) /* Include implementation details. */
|
|
|
|
extern "C" {
|
|
extern char *cplus_demangle (const char *mangled, int options);
|
|
}
|
|
|
|
std::string StackTrace::Demangle(const char *mangled) {
|
|
assert(mangled);
|
|
if (!mangled || !*mangled) {
|
|
return "";
|
|
}
|
|
|
|
size_t skip_first = 0;
|
|
if (mangled[0] == '.' || mangled[0] == '$') ++skip_first;
|
|
//if (mangled[skip_first] == '_') ++skip_first;
|
|
|
|
char *result = cplus_demangle(mangled + skip_first, DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE);
|
|
if (result == nullptr) return mangled;
|
|
|
|
string ret;
|
|
if (mangled[0] == '.') ret += '.';
|
|
ret += result;
|
|
free (result);
|
|
return ret;
|
|
}
|
|
|
|
void StackTraceNoHeap::Demangle(int fd, const char *mangled) {
|
|
assert(mangled);
|
|
if (!mangled || !*mangled) {
|
|
dprintf(fd, "??");
|
|
return ;
|
|
}
|
|
|
|
size_t skip_first = 0;
|
|
if (mangled[0] == '.' || mangled[0] == '$') ++skip_first;
|
|
//if (mangled[skip_first] == '_') ++skip_first;
|
|
|
|
char *result = cplus_demangle(mangled + skip_first,
|
|
DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE);
|
|
if (result == nullptr) {
|
|
dprintf(fd, "%s", mangled);
|
|
return;
|
|
}
|
|
dprintf(fd, "%s%s", mangled[0]=='.' ? "." : "", result);
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|