Arquivos
hhvm/hphp/util/stack_trace.cpp
T
Todd Nowacki adda9a5022 HPHP Changes for Clang
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
2013-06-25 13:19:03 -07:00

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 ;
}
///////////////////////////////////////////////////////////////////////////////
}