dde71705f4fc2294bd658cc7da30a0ed9008ecd3
[folly.git] / folly / experimental / symbolizer / StackTrace.cpp
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "folly/experimental/symbolizer/StackTrace.h"
18
19 // Must be first to ensure that UNW_LOCAL_ONLY is defined
20 #define UNW_LOCAL_ONLY 1
21 #include <libunwind.h>
22
23 namespace folly { namespace symbolizer {
24
25 ssize_t getStackTrace(uintptr_t* addresses, size_t maxAddresses) {
26   static_assert(sizeof(uintptr_t) == sizeof(void*),
27                 "uinptr_t / pointer size mismatch");
28   // The libunwind documentation says that unw_backtrace is async-signal-safe
29   // but, as of libunwind 1.0.1, it isn't (tdep_trace allocates memory on
30   // x86_64)
31   int r = unw_backtrace(reinterpret_cast<void**>(addresses), maxAddresses);
32   return r < 0 ? -1 : r;
33 }
34
35 namespace {
36 inline bool getFrameInfo(unw_cursor_t* cursor, uintptr_t& ip) {
37   unw_word_t uip;
38   if (unw_get_reg(cursor, UNW_REG_IP, &uip) < 0) {
39     return false;
40   }
41   int r = unw_is_signal_frame(cursor);
42   if (r < 0) {
43     return false;
44   }
45   // Use previous instruction in normal (call) frames (because the
46   // return address might not be in the same function for noreturn functions)
47   // but not in signal frames.
48   ip = uip - (r == 0);
49   return true;
50 }
51 }  // namespace
52
53 ssize_t getStackTraceSafe(uintptr_t* addresses, size_t maxAddresses) {
54   if (maxAddresses == 0) {
55     return 0;
56   }
57   unw_context_t context;
58   if (unw_getcontext(&context) < 0) {
59     return -1;
60   }
61   unw_cursor_t cursor;
62   if (unw_init_local(&cursor, &context) < 0) {
63     return -1;
64   }
65   if (!getFrameInfo(&cursor, *addresses)) {
66     return -1;
67   }
68   ++addresses;
69   ssize_t count = 1;
70   for (; count != maxAddresses; ++count, ++addresses) {
71     int r = unw_step(&cursor);
72     if (r < 0) {
73       return -1;
74     }
75     if (r == 0) {
76       break;
77     }
78     if (!getFrameInfo(&cursor, *addresses)) {
79       return -1;
80     }
81   }
82   return count;
83 }
84
85 }}  // namespaces