99
1010#include " xenia/cpu/stack_walker.h"
1111
12+ #include < condition_variable>
13+ #include < mutex>
14+ #include < unordered_set>
15+ #define UNW_LOCAL_ONLY
16+ #include < libunwind.h>
17+ #include < signal.h>
18+
19+ #include " stack_walker.h"
1220#include " xenia/base/logging.h"
21+ #include " xenia/cpu/backend/code_cache.h"
1322
1423namespace xe {
1524namespace cpu {
1625
26+ const int CAPTURE_SIGNAL = SIGUSR1;
27+
28+ struct PosixStackCapture {
29+ unw_context_t context_;
30+ std::vector<unw_cursor_t > cursors_;
31+
32+ PosixStackCapture (unw_context_t && context, size_t frame_offset,
33+ size_t frame_count)
34+ : context_(context) {
35+ unw_cursor_t cursor;
36+ unw_init_local (&cursor, &context);
37+
38+ for (size_t i = 0 ; i < frame_offset; ++i) {
39+ if (unw_step (&cursor) < 0 ) {
40+ return ;
41+ }
42+ }
43+ for (uint32_t i = 0 ; i < frame_count; ++i) {
44+ int step_result = unw_step (&cursor);
45+ if (step_result == 0 ) {
46+ break ;
47+ } else if (step_result < 0 ) {
48+ switch (-step_result) {
49+ case UNW_EUNSPEC:
50+ return ;
51+ case UNW_ENOINFO:
52+ return ;
53+ case UNW_EBADVERSION:
54+ return ;
55+ case UNW_EINVALIDIP:
56+ return ;
57+ case UNW_EBADFRAME:
58+ return ;
59+ case UNW_ESTOPUNWIND:
60+ return ;
61+ default :
62+ return ;
63+ }
64+ }
65+ cursors_.push_back (cursor);
66+ }
67+ }
68+ };
69+ } // namespace cpu
70+ } // namespace xe
71+
72+ namespace std {
73+ // TODO(bwrsandman): This needs to be expanded on to avoid collisions of stacks
74+ // with same ip and depth
75+ template <>
76+ struct hash <xe::cpu::PosixStackCapture> {
77+ typedef xe::cpu::PosixStackCapture argument_t ;
78+ typedef std::size_t result_t ;
79+ result_t operator ()(argument_t const & capture) const noexcept {
80+ if (capture.cursors_ .empty ()) {
81+ return 0 ;
82+ }
83+ result_t result = 0 ;
84+ unw_word_t ip;
85+ unw_cursor_t front_cursor = capture.cursors_ .front ();
86+ unw_get_reg (&front_cursor, UNW_REG_IP, &ip);
87+ auto h1 = std::hash<unw_word_t >{}(ip);
88+ auto h2 = std::hash<size_t >{}(capture.cursors_ .size ());
89+ return h1 ^ (h2 << 1 );
90+ }
91+ };
92+
93+ template <>
94+ struct equal_to <xe::cpu::PosixStackCapture> {
95+ typedef xe::cpu::PosixStackCapture argument_t ;
96+ typedef std::size_t result_t ;
97+ result_t operator ()(argument_t const & capture_1,
98+ argument_t const & capture_2) const noexcept {
99+ if (capture_1.cursors_ .size () != capture_2.cursors_ .size ()) {
100+ return false ;
101+ }
102+ unw_word_t reg_1, reg_2;
103+ unw_cursor_t front_cursor_1 = capture_1.cursors_ .front ();
104+ unw_cursor_t front_cursor_2 = capture_2.cursors_ .front ();
105+ for (uint32_t i = 0 ; i < UNW_REG_LAST; ++i) {
106+ unw_get_reg (&front_cursor_1, i, ®_1);
107+ unw_get_reg (&front_cursor_2, i, ®_2);
108+ if (reg_1 != reg_2) {
109+ return false ;
110+ }
111+ }
112+ return true ;
113+ }
114+ };
115+ } // namespace std
116+
117+ namespace xe {
118+ namespace cpu {
119+
120+ #include < libiberty/demangle.h>
121+ #include < memory>
122+ std::string demangle (const char * mangled_name) {
123+ if (mangled_name == std::string ()) {
124+ return " " ;
125+ }
126+ std::unique_ptr<char , decltype (&std::free)> ptr (
127+ cplus_demangle (mangled_name, DMGL_NO_OPTS), &std::free);
128+ return ptr ? ptr.get () : mangled_name;
129+ }
130+
131+ static struct signal_handler_arg_t {
132+ std::mutex mutex;
133+ std::condition_variable cv;
134+ volatile bool set;
135+ unw_context_t context_;
136+ } signal_handler_arg = {};
137+
138+ class PosixStackWalker : public StackWalker {
139+ public:
140+ explicit PosixStackWalker (backend::CodeCache* code_cache) {
141+ // Get the boundaries of the code cache so we can quickly tell if a symbol
142+ // is ours or not.
143+ // We store these globally so that the Sym* callbacks can access them.
144+ // They never change, so it's fine even if they are touched from multiple
145+ // threads.
146+ // code_cache_ = code_cache;
147+ // code_cache_min_ = code_cache_->base_address();
148+ // code_cache_max_ = code_cache_->base_address() +
149+ // code_cache_->total_size();
150+ }
151+
152+ bool Initialize () { return true ; }
153+
154+ size_t CaptureStackTrace (uint64_t * frame_host_pcs, size_t frame_offset,
155+ size_t frame_count,
156+ uint64_t * out_stack_hash) override {
157+ if (out_stack_hash) {
158+ *out_stack_hash = 0 ;
159+ }
160+
161+ unw_context_t context;
162+ if (unw_getcontext (&context) != 0 ) {
163+ return false ;
164+ }
165+
166+ auto capture =
167+ PosixStackCapture (std::move (context), frame_offset, frame_count);
168+
169+ for (uint32_t i = 0 ; i < capture.cursors_ .size (); ++i) {
170+ frame_host_pcs[i] = reinterpret_cast <uintptr_t >(&capture.cursors_ [i]);
171+ }
172+ // Two identical stack traces will generate identical hash values.
173+ if (out_stack_hash) {
174+ *out_stack_hash = captures_.hash_function ()(capture);
175+ }
176+
177+ auto size = capture.cursors_ .size ();
178+
179+ captures_.insert (std::move (capture));
180+
181+ return size;
182+ }
183+
184+ size_t CaptureStackTrace (void * thread_handle, uint64_t * frame_host_pcs,
185+ size_t frame_offset, size_t frame_count,
186+ const X64Context* in_host_context,
187+ X64Context* out_host_context,
188+ uint64_t * out_stack_hash) override {
189+ if (out_stack_hash) {
190+ *out_stack_hash = 0 ;
191+ }
192+
193+ // Install signal capture
194+ struct sigaction action {};
195+ struct sigaction previous_action {};
196+ action.sa_flags = SA_SIGINFO;
197+ action.sa_sigaction = [](int signal, siginfo_t * info, void * context) {
198+ std::unique_lock<std::mutex> lock (signal_handler_arg.mutex );
199+ unw_getcontext (&signal_handler_arg.context_ );
200+ signal_handler_arg.set = true ;
201+ signal_handler_arg.cv .notify_one ();
202+ };
203+ sigemptyset (&action.sa_mask );
204+ sigaction (CAPTURE_SIGNAL, &action, &previous_action);
205+
206+ // Send signal
207+ pthread_kill (reinterpret_cast <pthread_t >(thread_handle), CAPTURE_SIGNAL);
208+
209+ // Wait to have data back
210+ unw_context_t uc;
211+ {
212+ std::unique_lock<std::mutex> lock (signal_handler_arg.mutex );
213+ signal_handler_arg.cv .wait (lock);
214+ uc = signal_handler_arg.context_ ;
215+ signal_handler_arg.set = false ;
216+ }
217+
218+ // Restore original handler if it existed
219+ sigaction (CAPTURE_SIGNAL, &previous_action, nullptr );
220+
221+ // Skip signal callback frame
222+ ++frame_offset;
223+
224+ auto capture = PosixStackCapture (std::move (signal_handler_arg.context_ ),
225+ frame_offset, frame_count);
226+
227+ for (uint32_t i = 0 ; i < capture.cursors_ .size (); ++i) {
228+ frame_host_pcs[i] = reinterpret_cast <uintptr_t >(&capture.cursors_ [i]);
229+ }
230+ // Two identical stack traces will generate identical hash values.
231+ if (out_stack_hash) {
232+ *out_stack_hash = captures_.hash_function ()(capture);
233+ }
234+
235+ auto size = capture.cursors_ .size ();
236+
237+ captures_.insert (std::move (capture));
238+
239+ return size;
240+ }
241+
242+ bool ResolveStack (uint64_t * frame_host_pcs, StackFrame* frames,
243+ size_t frame_count) override {
244+ for (size_t i = 0 ; i < frame_count; ++i) {
245+ auto & frame = frames[i];
246+ unw_cursor_t & cursor =
247+ *reinterpret_cast <unw_cursor_t *>(frame_host_pcs[i]);
248+ std::memset (&frame, 0 , sizeof (frame));
249+ frame.host_pc = frame_host_pcs[i];
250+
251+ // If in the generated range, we know it's ours.
252+ // if (frame.host_pc >= code_cache_min_ && frame.host_pc <
253+ // code_cache_max_) {
254+ //
255+ // } else {
256+ // // Host symbol, which means either emulator or system.
257+ // frame.type = StackFrame::Type::kHost;
258+ // }
259+ std::array<char , sizeof (frame.host_symbol .name )> host_symbol_name = {};
260+ auto ok = unw_get_proc_name (&cursor, host_symbol_name.data (),
261+ host_symbol_name.size (),
262+ &frame.host_symbol .address );
263+ switch (ok) {
264+ case UNW_ESUCCESS:
265+ break ;
266+ case UNW_EUNSPEC:
267+ return false ;
268+ case UNW_ENOINFO:
269+ return false ;
270+ case UNW_ENOMEM:
271+ return false ;
272+ default :
273+ return false ;
274+ }
275+ auto demangled_host_symbol_name = demangle (host_symbol_name.data ());
276+ std::strncpy (frame.host_symbol .name , demangled_host_symbol_name.c_str (),
277+ sizeof (frame.host_symbol .name ));
278+ // unw_proc_info_t info = {};
279+ // ok = unw_get_proc_info(&cursor, &info);
280+ }
281+ return true ;
282+ }
283+
284+ // static xe::cpu::backend::CodeCache* code_cache_;
285+ // static uint32_t code_cache_min_;
286+ // static uint32_t code_cache_max_;
287+ std::unordered_set<PosixStackCapture> captures_;
288+ };
289+
17290std::unique_ptr<StackWalker> StackWalker::Create (
18291 backend::CodeCache* code_cache) {
19- XELOGD (" Stack walker unimplemented on posix" );
20- return nullptr ;
292+ auto stack_walker = std::make_unique<PosixStackWalker>(code_cache);
293+ if (!stack_walker->Initialize ()) {
294+ XELOGE (" Unable to initialize stack walker: debug/save states disabled" );
295+ return nullptr ;
296+ }
297+ return std::unique_ptr<StackWalker>(stack_walker.release ());
21298}
22299
23300} // namespace cpu
24- } // namespace xe
301+ } // namespace xe
0 commit comments