Fix semdiff syntactic output
[hiphop-php.git] / hphp / util / stack-trace.cpp
blobc493e8151fccd53639330026f3b24fc25afa9070
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/util/stack-trace.h"
18 #include <fstream>
19 #include <mutex>
20 #include <set>
21 #include <string>
23 #ifndef _MSC_VER
24 #include <execinfo.h>
25 #endif
27 #include <signal.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
32 #include <folly/Conv.h>
33 #include <folly/Demangle.h>
34 #include <folly/FileUtil.h>
35 #include <folly/Format.h>
36 #include <folly/ScopeGuard.h>
37 #include <folly/String.h>
39 #include "hphp/util/assertions.h"
40 #include "hphp/util/compatibility.h"
41 #include "hphp/util/conv-10.h"
42 #include "hphp/util/hash-map-typedefs.h"
43 #include "hphp/util/hash.h"
44 #include "hphp/util/process.h"
45 #include "hphp/util/thread-local.h"
47 #if defined USE_FOLLY_SYMBOLIZER
49 #include <folly/experimental/symbolizer/Symbolizer.h>
51 #elif defined HAVE_LIBBFD
53 #include <bfd.h>
55 #endif
57 namespace HPHP {
58 ////////////////////////////////////////////////////////////////////////////////
60 const char* const s_defaultBlacklist[] = {
61 "_ZN4HPHP16StackTraceNoHeap",
62 "_ZN5folly10symbolizer17getStackTraceSafeEPmm",
65 bool StackTraceBase::Enabled = true;
67 const char* const* StackTraceBase::FunctionBlacklist = s_defaultBlacklist;
68 unsigned StackTraceBase::FunctionBlacklistCount = 2;
70 ////////////////////////////////////////////////////////////////////////////////
72 namespace {
74 void printStr(int fd, folly::StringPiece s) {
75 write(fd, s.begin(), s.size());
78 void printInt(int fd, int64_t val) {
79 char buf[24];
80 printStr(fd, conv_10(val, buf + sizeof buf));
83 void printFrameHdr(int fd, int fr) {
84 printStr(fd, "# ");
85 printInt(fd, fr);
86 printStr(fd, (fr < 10 ? " " : " "));
89 void printPair(int fd,
90 folly::StringPiece first,
91 folly::StringPiece second) {
92 printStr(fd, first);
93 write(fd, ": ", 2);
94 printStr(fd, second);
95 write(fd, "\n", 1);
98 void printPair(int fd,
99 folly::StringPiece first,
100 int64_t second) {
101 char buf[24];
102 printPair(fd, first, conv_10(second, buf + sizeof buf));
106 * Writes a file path to a file while folding any consecutive forward slashes.
108 void write_path(int fd, folly::StringPiece path) {
109 while (!path.empty()) {
110 auto const pos = path.find('/');
111 if (pos == std::string::npos) {
112 folly::writeNoInt(fd, path.data(), path.size());
113 break;
116 auto const left = path.subpiece(0, pos + 1);
117 folly::writeNoInt(fd, left.data(), left.size());
119 auto right = path.subpiece(pos);
120 while (!right.empty() && right[0] == '/') {
121 right = right.subpiece(1);
124 path = right;
128 bool isBlacklisted(const char* funcname) {
129 for (int i = 0; i < StackTraceBase::FunctionBlacklistCount; i++) {
130 auto ignoreFunc = StackTraceBase::FunctionBlacklist[i];
131 if (strncmp(funcname, ignoreFunc, strlen(ignoreFunc)) == 0) {
132 return true;
135 return false;
139 * Demangle a function name and return it as a string.
141 std::string demangle(const char* mangled) {
142 auto const skip_first =
143 static_cast<int>(mangled[0] == '.' || mangled[0] == '$');
145 auto const result = folly::demangle(mangled + skip_first);
146 auto const ret = std::string(result.data(), result.size());
147 if (mangled[0] == '.') {
148 return "." + ret;
151 return ret;
155 * Demangle a function name and write it to a file.
157 void demangle(int fd, const char* mangled) {
158 if (mangled == nullptr || mangled[0] == '\0') {
159 write(fd, "??", 2);
160 return;
163 auto const skip_first =
164 static_cast<int>(mangled[0] == '.' || mangled[0] == '$');
166 char buf[2048];
167 auto sz = folly::demangle(mangled + skip_first, buf, sizeof buf);
168 if (sz > sizeof buf) {
169 write(fd, mangled, strlen(mangled));
170 return;
173 if (mangled[0] == '.') write(fd, ".", 1);
174 write(fd, buf, sz);
177 std::mutex s_perfMapCacheMutex;
178 StackTrace::PerfMap s_perfMapCache;
179 std::set<void*> s_perfMapNegCache;
181 void translateFromPerfMap(StackFrameExtra* frame) {
182 std::lock_guard<std::mutex> lock(s_perfMapCacheMutex);
184 if (s_perfMapCache.translate(frame)) {
185 if (s_perfMapNegCache.count(frame->addr)) {
186 // A prior failed lookup of frame already triggered a rebuild.
187 return;
189 // Rebuild the cache, then search again.
190 s_perfMapCache.rebuild();
191 if (s_perfMapCache.translate(frame)) {
192 s_perfMapNegCache.insert(frame->addr);
197 template <bool safe>
198 int ALWAYS_INLINE get_backtrace(void** frame, int max) {
199 int ret = 0;
200 #if defined USE_FOLLY_SYMBOLIZER
201 if (safe) {
202 ret = folly::symbolizer::getStackTraceSafe((uintptr_t*)frame, max);
203 } else {
204 ret = folly::symbolizer::getStackTrace((uintptr_t*)frame, max);
206 #elif defined __GLIBC__
207 ret = backtrace(frame, max);
208 #endif
209 if (ret < 0 || ret > max) {
210 return 0;
212 return ret;
217 struct StackTraceLog {
218 hphp_string_map<std::string> data;
220 static THREAD_LOCAL(StackTraceLog, s_logData);
222 THREAD_LOCAL(StackTraceLog, StackTraceLog::s_logData);
224 ////////////////////////////////////////////////////////////////////////////////
226 StackTraceBase::StackTraceBase() {
227 #if !defined USE_FOLLY_SYMBOLIZER && defined HAVE_LIBBFD
228 bfd_init();
229 #endif
232 StackTrace::StackTrace(bool trace) {
233 if (trace && Enabled) {
234 m_frames.resize(kMaxFrame);
235 m_frames.resize(get_backtrace<false>(m_frames.data(), kMaxFrame));
239 StackTrace::StackTrace(StackTrace::Force) {
240 m_frames.resize(kMaxFrame);
241 m_frames.resize(get_backtrace<false>(m_frames.data(), kMaxFrame));
244 StackTrace::StackTrace(void* const* ips, size_t count) {
245 m_frames.resize(count);
246 m_frames.assign(ips, ips + count);
249 StackTrace::StackTrace(folly::StringPiece hexEncoded) {
250 // Can't split into StringPieces, strtoll() expects a null terminated string.
251 std::vector<std::string> frames;
252 folly::split(':', hexEncoded, frames);
253 for (auto const& frame : frames) {
254 m_frames.push_back((void*)strtoll(frame.c_str(), nullptr, 16));
258 void StackTrace::get(std::vector<std::shared_ptr<StackFrameExtra>>&
259 frames) const {
260 frames.clear();
261 for (auto const frame_ptr : m_frames) {
262 frames.push_back(Translate(frame_ptr));
266 std::string StackTrace::hexEncode(int minLevel /* = 0 */,
267 int maxLevel /* = 999 */) const {
268 std::string bts;
269 for (int i = minLevel; i < (int)m_frames.size() && i < maxLevel; i++) {
270 if (i > minLevel) bts += ':';
271 char buf[20];
272 snprintf(buf, sizeof(buf), "%" PRIx64, (int64_t)m_frames[i]);
273 bts.append(buf);
275 return bts;
278 const std::string& StackTrace::toString(int skip, int limit) const {
279 auto usable = skip == 0 && limit == -1;
280 if (!usable || !m_trace_usable) {
281 m_trace.clear();
284 if (!m_trace.empty()) return m_trace;
286 size_t frame = 0;
287 for (auto const frame_ptr : m_frames) {
288 auto const framename = Translate(frame_ptr)->toString();
289 // Ignore frames in the StackTrace class.
290 if (framename.find("StackTrace::") != std::string::npos) {
291 continue;
293 if (skip-- > 0) continue;
294 m_trace += "# ";
295 m_trace += folly::to<std::string>(frame);
296 if (frame < 10) m_trace += " ";
298 m_trace += " ";
299 m_trace += framename;
300 m_trace += "\n";
301 ++frame;
302 if ((int)frame == limit) break;
305 m_trace_usable = usable;
306 return m_trace;
309 void StackTrace::PerfMap::rebuild() {
310 m_map.clear();
312 char filename[64];
313 snprintf(filename, sizeof(filename), "/tmp/perf-%d.map", getpid());
315 std::ifstream perf_map(filename);
316 if (!perf_map) return;
318 std::string line;
320 while (!perf_map.bad() && !perf_map.eof()) {
321 if (!std::getline(perf_map, line)) continue;
323 uintptr_t addr;
324 uint32_t size;
325 int name_pos = 0;
326 // We compare < 2 because some implementations of sscanf() increment the
327 // assignment count for %n against the specification.
328 if (sscanf(line.c_str(), "%lx %x %n", &addr, &size, &name_pos) < 2 ||
329 name_pos < 4 /* not assigned, or not enough spaces */) {
330 continue;
332 if (name_pos >= line.size()) continue;
334 auto const past = addr + size;
335 auto const range = PerfMap::Range { addr, past };
337 m_map[range] = line.substr(name_pos);
340 m_built = true;
343 bool StackTrace::PerfMap::translate(StackFrameExtra* frame) const {
344 if (!m_built) {
345 const_cast<StackTrace::PerfMap*>(this)->rebuild();
348 // Use a key with non-zero span, because otherwise a key right at the base of
349 // a range will be treated as before the range (bad) rather than within it
350 // (good).
351 PerfMap::Range key{uintptr_t(frame->addr), uintptr_t(frame->addr)+1};
352 auto const& it = m_map.find(key);
353 if (it == m_map.end()) {
354 // Not found.
355 frame->filename = "?";
356 frame->funcname = "TC?"; // Note HHProf::HandlePProfSymbol() dependency.
357 return true;
360 // Found.
361 auto const& filefunc = it->second;
362 auto const colon_pos = filefunc.find("::");
363 if (colon_pos == std::string::npos) {
364 frame->funcname = filefunc;
365 return false;
368 auto const prefix = std::string(filefunc, 0, colon_pos);
369 if (prefix == "HHVM") {
370 // HHVM unique stubs are simply "HHVM::stubName".
371 frame->funcname = filefunc + "()";
372 return false;
375 if (prefix != "PHP") {
376 // We don't recognize the prefix, so don't do any munging.
377 frame->funcname = filefunc;
378 return false;
381 // Jitted PHP functions have the format "PHP::file.php::Full::funcName".
382 auto const file_pos = colon_pos + 2;
383 auto const file_end_pos = filefunc.find("::", file_pos);
384 if (file_end_pos == std::string::npos) {
385 // Bad PHP function descriptor.
386 frame->funcname = filefunc;
387 return false;
389 frame->filename = std::string(filefunc, file_pos, file_end_pos - file_pos);
390 frame->funcname = "PHP::" + std::string(filefunc, file_end_pos + 2) + "()";
391 return false;
394 ////////////////////////////////////////////////////////////////////////////////
396 std::string StackFrameExtra::toString() const {
397 constexpr folly::StringPiece qq{"??"};
398 return folly::sformat(
399 "{} at {}:{}",
400 funcname.empty() ? qq : folly::StringPiece{funcname},
401 filename.empty() ? qq : folly::StringPiece{filename},
402 lineno
406 ////////////////////////////////////////////////////////////////////////////////
408 StackTraceNoHeap::StackTraceNoHeap(bool trace) {
409 if (trace && Enabled) {
410 m_frame_count = get_backtrace<true>(m_frames, kMaxFrame);
414 void StackTraceNoHeap::AddExtraLogging(const char* name,
415 const std::string& value) {
416 assertx(name != nullptr && name[0] != '\0');
417 StackTraceLog::s_logData->data[name] = value;
420 void StackTraceNoHeap::ClearAllExtraLogging() {
421 StackTraceLog::s_logData->data.clear();
424 void StackTraceNoHeap::log(const char* errorType, int fd, const char* buildId,
425 int debuggerCount) const {
426 assert(fd >= 0);
428 printPair(fd, "Host", Process::GetHostName().c_str());
429 printPair(fd, "ProcessID", (int64_t)getpid());
430 printPair(fd, "ThreadID", (int64_t)Process::GetThreadId());
431 printPair(fd, "ThreadPID", Process::GetThreadPid());
432 printPair(fd, "Name", Process::GetAppName().c_str());
433 printPair(fd, "Type", errorType ? errorType : "(unknown error)");
434 printPair(fd, "Runtime", "hhvm");
435 printPair(fd, "Version", buildId);
436 printPair(fd, "DebuggerCount", debuggerCount);
437 write(fd, "\n", 1);
439 for (auto const& pair : StackTraceLog::s_logData->data) {
440 printPair(fd, pair.first.c_str(), pair.second.c_str());
442 write(fd, "\n", 1);
444 printStackTrace(fd);
447 ////////////////////////////////////////////////////////////////////////////////
449 #if defined USE_FOLLY_SYMBOLIZER
451 void StackTraceNoHeap::printStackTrace(int fd) const {
452 folly::symbolizer::Symbolizer symbolizer;
453 folly::symbolizer::SymbolizedFrame frames[kMaxFrame];
454 symbolizer.symbolize((uintptr_t*)m_frames, frames, m_frame_count);
455 for (int i = 0, fr = 0; i < m_frame_count; i++) {
456 auto const& frame = frames[i];
457 if (!frame.name ||
458 !frame.name[0] ||
459 isBlacklisted(frame.name)) {
460 continue;
462 printFrameHdr(fd, fr);
463 demangle(fd, frame.name);
464 if (frame.location.hasFileAndLine) {
465 char fileBuf[PATH_MAX];
466 fileBuf[0] = '\0';
467 frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
468 printStr(fd, " at ");
469 write_path(fd, fileBuf);
470 printStr(fd, ":");
471 printInt(fd, frame.location.line);
473 printStr(fd, "\n");
474 fr++;
478 std::shared_ptr<StackFrameExtra> StackTrace::Translate(void* frame_addr,
479 PerfMap* pm) {
480 folly::symbolizer::Symbolizer symbolizer;
481 folly::symbolizer::SymbolizedFrame sf;
482 symbolizer.symbolize((uintptr_t*)&frame_addr, &sf, 1);
484 auto frame = std::make_shared<StackFrameExtra>(frame_addr);
485 if (!sf.found ||
486 !sf.name ||
487 !sf.name[0]) {
488 // Lookup failed, so this is probably a PHP symbol. Let's
489 // check the perf map.
490 if (pm != nullptr) {
491 pm->translate(frame.get());
492 } else {
493 translateFromPerfMap(frame.get());
495 return frame;
498 if (sf.location.hasFileAndLine) {
499 frame->filename = sf.location.file.toString();
500 frame->lineno = sf.location.line;
503 frame->funcname = sf.name ? demangle(sf.name) : "";
505 return frame;
508 #elif defined HAVE_LIBBFD
510 namespace {
511 ////////////////////////////////////////////////////////////////////////////////
513 constexpr int MaxKey = 100;
515 struct BfdCache {
516 bfd* abfd{nullptr};
517 asymbol** syms{nullptr};
519 ~BfdCache() {
520 if (abfd == nullptr) return;
521 bfd_cache_close(abfd);
522 bfd_free_cached_info(abfd);
523 bfd_close_all_done(abfd);
527 struct NamedBfd {
528 BfdCache bc;
529 char key[MaxKey];
532 using NamedBfdRange = folly::Range<NamedBfd*>;
534 struct Addr2lineData {
535 asymbol** syms;
536 bfd_vma pc;
537 const char* filename{nullptr};
538 const char* functionname{nullptr};
539 unsigned line{0};
540 bfd_boolean found{FALSE};
543 using BfdMap = hphp_hash_map<
544 std::string,
545 std::shared_ptr<BfdCache>,
546 string_hash
550 * We cache opened bfd file pointers that in turn cache frame pointer lookup
551 * tables.
553 std::mutex s_bfdMutex;
554 BfdMap s_bfds;
556 ////////////////////////////////////////////////////////////////////////////////
558 /* Copied and re-factored from addr2line. */
560 void find_address_in_section(bfd* abfd, asection* section, void* data) {
561 auto adata = reinterpret_cast<Addr2lineData*>(data);
562 if (adata->found) {
563 return;
566 if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) {
567 return;
570 auto const vma = bfd_get_section_vma(abfd, section);
571 if (adata->pc < vma) {
572 return;
575 auto const size = bfd_get_section_size(section);
576 if (adata->pc >= vma + size) {
577 return;
580 // libdwarf allocates its own unaligned memory so it doesn't play well with
581 // valgrind.
582 #ifndef VALGRIND
583 adata->found = bfd_find_nearest_line(
584 abfd, section, adata->syms, adata->pc - vma, &adata->filename,
585 &adata->functionname, &adata->line
587 #endif
589 if (adata->found) {
590 auto file = adata->filename;
591 auto line = adata->line;
592 bfd_boolean found = TRUE;
593 while (found) {
594 found = bfd_find_inliner_info(abfd, &file, &adata->functionname, &line);
599 bool slurp_symtab(asymbol*** syms, bfd* abfd) {
600 unsigned size;
602 auto symcount = bfd_read_minisymbols(abfd, FALSE, (void**)syms, &size);
603 if (symcount == 0) {
604 symcount = bfd_read_minisymbols(
605 abfd, TRUE /* dynamic */, (void**)syms, &size
608 return symcount >= 0;
611 bool translate_addresses(bfd* abfd, const char* addr, Addr2lineData* adata) {
612 if (abfd == nullptr) return false;
613 adata->pc = bfd_scan_vma(addr, nullptr, 16);
615 adata->found = FALSE;
616 bfd_map_over_sections(abfd, find_address_in_section, adata);
618 if (!adata->found || !adata->functionname || !*adata->functionname) {
619 return false;
621 return true;
624 bool fill_bfd_cache(folly::StringPiece filename, BfdCache& p) {
625 // Hard to avoid heap here!
626 auto abfd = bfd_openr(filename.begin(), nullptr);
627 if (abfd == nullptr) return true;
629 // Some systems don't have the BFD_DECOMPRESS flag.
630 #ifdef BFD_DECOMPRESS
631 abfd->flags |= BFD_DECOMPRESS;
632 #endif
634 p.abfd = nullptr;
635 p.syms = nullptr;
636 char** match;
637 if (bfd_check_format(abfd, bfd_archive) ||
638 !bfd_check_format_matches(abfd, bfd_object, &match) ||
639 !slurp_symtab(&p.syms, abfd)) {
640 bfd_close(abfd);
641 return true;
643 p.abfd = abfd;
644 return false;
647 std::shared_ptr<BfdCache> get_bfd_cache(folly::StringPiece filename) {
648 // Heterogeneous lookup is in C++14. Otherwise we'll end up making a
649 // std::string copy.
650 auto iter = std::find_if(
651 s_bfds.begin(),
652 s_bfds.end(),
653 [&] (const BfdMap::value_type& pair) { return pair.first == filename; }
656 if (iter != s_bfds.end()) {
657 return iter->second;
660 auto p = std::make_shared<BfdCache>();
661 if (fill_bfd_cache(filename, *p)) {
662 p.reset();
664 s_bfds[filename.str()] = p;
665 return p;
668 BfdCache* get_bfd_cache(folly::StringPiece filename, NamedBfdRange bfds) {
669 auto probe = hash_string_cs(filename.begin(), filename.size()) % bfds.size();
671 // Match on the end of filename instead of the beginning, if necessary.
672 if (filename.size() >= MaxKey) {
673 filename = filename.subpiece(filename.size() - MaxKey + 1);
676 while (bfds[probe].key[0] && strcmp(filename.begin(), bfds[probe].key) != 0) {
677 probe = probe ? probe-1 : bfds.size()-1;
680 auto p = &bfds[probe].bc;
681 if (bfds[probe].key[0]) return p;
683 assert(filename.size() < MaxKey);
684 assert(filename.begin()[filename.size()] == 0);
685 // Accept the rare collision on keys (requires probe collision too).
686 memcpy(bfds[probe].key, filename.begin(), filename.size() + 1);
687 fill_bfd_cache(filename, *p);
688 return p;
692 * Run addr2line to translate a function pointer into function name and line
693 * number.
695 bool addr2line(folly::StringPiece filename, StackFrame* frame,
696 Addr2lineData* data, NamedBfdRange bfds) {
697 char address[32];
698 snprintf(address, sizeof(address), "%p", frame->addr);
700 std::lock_guard<std::mutex> lock(s_bfdMutex);
701 data->filename = nullptr;
702 data->functionname = nullptr;
703 data->line = 0;
704 bool ret;
706 if (bfds.empty()) {
707 auto p = get_bfd_cache(filename);
708 data->syms = p->syms;
709 ret = translate_addresses(p->abfd, address, data);
710 } else {
711 // Don't let shared_ptr malloc behind the scenes in this case.
712 auto q = get_bfd_cache(filename, bfds);
713 data->syms = q->syms;
714 ret = translate_addresses(q->abfd, address, data);
717 if (ret) {
718 frame->lineno = data->line;
720 return ret;
724 * Translate a frame pointer to file name and line number pair.
726 bool translate(StackFrame* frame, Dl_info& dlInfo, Addr2lineData* data,
727 NamedBfdRange bfds = NamedBfdRange()) {
728 if (!dladdr(frame->addr, &dlInfo)) {
729 return false;
732 // Frame pointer offset in previous frame.
733 frame->offset = (char*)frame->addr - (char*)dlInfo.dli_saddr;
735 if (dlInfo.dli_fname) {
736 // First attempt without offsetting base address.
737 if (!addr2line(dlInfo.dli_fname, frame, data, bfds) &&
738 dlInfo.dli_fname && strstr(dlInfo.dli_fname, ".so")) {
739 // Offset shared lib's base address.
741 frame->addr = (char*)frame->addr - (size_t)dlInfo.dli_fbase;
743 // Use addr2line to get line number info.
744 addr2line(dlInfo.dli_fname, frame, data, bfds);
747 return true;
750 ///////////////////////////////////////////////////////////////////////////////
753 * Variant of translate() used by StackTraceNoHeap.
755 bool translate(int fd, void* frame_addr, int frame_num, NamedBfdRange bfds) {
756 // Frame pointer offset in previous frame.
757 Dl_info dlInfo;
758 Addr2lineData adata;
759 StackFrame frame(frame_addr);
760 if (!translate(&frame, dlInfo, &adata, bfds)) {
761 return false;
764 auto filename = adata.filename ? adata.filename : dlInfo.dli_fname;
765 if (filename == nullptr) filename = "??";
766 auto funcname = adata.functionname ? adata.functionname : dlInfo.dli_sname;
767 if (funcname == nullptr) funcname = "??";
769 // Ignore some frames that are always present.
770 if (isBlacklisted(funcname)) return false;
772 printFrameHdr(fd, frame_num);
773 demangle(fd, funcname);
774 printStr(fd, " at ");
775 write_path(fd, filename);
776 printStr(fd, ":");
777 printInt(fd, frame.lineno);
778 printStr(fd, "\n");
780 return true;
783 ////////////////////////////////////////////////////////////////////////////////
786 std::shared_ptr<StackFrameExtra> StackTrace::Translate(void* frame_addr,
787 PerfMap* pm) {
788 Dl_info dlInfo;
789 Addr2lineData adata;
791 auto frame = std::make_shared<StackFrameExtra>(frame_addr);
792 if (!translate(frame.get(), dlInfo, &adata)) {
793 // Lookup using dladdr() failed, so this is probably a PHP symbol. Let's
794 // check the perf map.
795 if (pm != nullptr) {
796 pm->translate(frame.get());
797 } else {
798 translateFromPerfMap(frame.get());
800 return frame;
803 if (adata.filename) {
804 frame->filename = adata.filename;
806 if (adata.functionname) {
807 frame->funcname = demangle(adata.functionname);
809 if (frame->filename.empty() && dlInfo.dli_fname) {
810 frame->filename = dlInfo.dli_fname;
812 if (frame->funcname.empty() && dlInfo.dli_sname) {
813 frame->funcname = demangle(dlInfo.dli_sname);
816 return frame;
819 void StackTraceNoHeap::printStackTrace(int fd) const {
820 // m_frame_count must be an upper bound on the number of filenames then *2 for
821 // tolerable hash table behavior.
822 auto const size = m_frame_count * 2;
824 // Using the heap in "NoHeap" is bad but we do it anyway.
825 const std::unique_ptr<NamedBfd[]> bfds(new NamedBfd[size]);
826 for (unsigned i = 0; i < size; i++) {
827 bfds.get()[i].key[0] = '\0';
830 int frame = 0;
831 for (unsigned i = 0; i < m_frame_count; i++) {
832 auto range = NamedBfdRange(bfds.get(), size);
833 if (translate(fd, m_frames[i], frame, range)) {
834 frame++;
837 // ~bfds[i].bc here (unlike the heap case).
840 ////////////////////////////////////////////////////////////////////////////////
842 #else // No libbfd or folly::Symbolizer
844 namespace {
846 void printHex(int fd, uint64_t val) {
847 char buf[16];
848 auto ptr = buf + sizeof buf;
850 while (ptr > buf) {
851 auto ch = val & 0xf;
852 *--ptr = ch + (ch >= 10 ? 'a' - 10 : '0');
853 val >>= 4;
856 printStr(fd, folly::StringPiece(buf, sizeof buf));
861 std::shared_ptr<StackFrameExtra> StackTrace::Translate(void* bt, PerfMap* pm) {
862 return std::make_shared<StackFrameExtra>(bt);
865 void StackTraceNoHeap::printStackTrace(int fd) const {
866 for (int i = 0; i < m_frame_count; i++) {
867 printStr(fd, "# ");
868 printInt(fd, i);
869 printStr(fd, (i < 10 ? " " : " "));
870 printHex(fd, (uintptr_t)m_frames[i]);
871 printStr(fd, "\n");
875 #endif
877 ////////////////////////////////////////////////////////////////////////////////