c58af4a7c43705992cecb186dcc48ebe3d50df44
[folly.git] / folly / experimental / symbolizer / StackTrace.cpp
1 /*
2  * Copyright 2017 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 {
24 namespace symbolizer {
25
26 ssize_t getStackTrace(uintptr_t* addresses, size_t maxAddresses) {
27   static_assert(
28       sizeof(uintptr_t) == sizeof(void*), "uinptr_t / pointer size mismatch");
29   // The libunwind documentation says that unw_backtrace is async-signal-safe
30   // but, as of libunwind 1.0.1, it isn't (tdep_trace allocates memory on
31   // x86_64)
32   int r = unw_backtrace(reinterpret_cast<void**>(addresses), maxAddresses);
33   return r < 0 ? -1 : r;
34 }
35
36 namespace {
37 inline bool getFrameInfo(unw_cursor_t* cursor, uintptr_t& ip) {
38   unw_word_t uip;
39   if (unw_get_reg(cursor, UNW_REG_IP, &uip) < 0) {
40     return false;
41   }
42   int r = unw_is_signal_frame(cursor);
43   if (r < 0) {
44     return false;
45   }
46   // Use previous instruction in normal (call) frames (because the
47   // return address might not be in the same function for noreturn functions)
48   // but not in signal frames.
49   ip = uip - (r == 0);
50   return true;
51 }
52 } // namespace
53
54 ssize_t getStackTraceSafe(uintptr_t* addresses, size_t maxAddresses) {
55   if (maxAddresses == 0) {
56     return 0;
57   }
58   unw_context_t context;
59   if (unw_getcontext(&context) < 0) {
60     return -1;
61   }
62   unw_cursor_t cursor;
63   if (unw_init_local(&cursor, &context) < 0) {
64     return -1;
65   }
66   if (!getFrameInfo(&cursor, *addresses)) {
67     return -1;
68   }
69   ++addresses;
70   size_t count = 1;
71   for (; count != maxAddresses; ++count, ++addresses) {
72     int r = unw_step(&cursor);
73     if (r < 0) {
74       return -1;
75     }
76     if (r == 0) {
77       break;
78     }
79     if (!getFrameInfo(&cursor, *addresses)) {
80       return -1;
81     }
82   }
83   return count;
84 }
85 } // namespace symbolizer
86 } // namespace folly