1// Copyright 2017 The Abseil Authors. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14// 15// Portable implementation - just use glibc 16// 17// Note: The glibc implementation may cause a call to malloc. 18// This can cause a deadlock in HeapProfiler. 19 20#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ 21#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ 22 23#include <emscripten.h> 24 25#include <atomic> 26#include <cstring> 27 28#include "absl/base/attributes.h" 29#include "absl/debugging/stacktrace.h" 30 31extern "C" { 32uintptr_t emscripten_stack_snapshot(); 33uint32_t emscripten_stack_unwind_buffer(uintptr_t pc, void *buffer, 34 uint32_t depth); 35} 36 37// Sometimes, we can try to get a stack trace from within a stack 38// trace, which can cause a self-deadlock. 39// Protect against such reentrant call by failing to get a stack trace. 40// 41// We use __thread here because the code here is extremely low level -- it is 42// called while collecting stack traces from within malloc and mmap, and thus 43// can not call anything which might call malloc or mmap itself. 44static __thread int recursive = 0; 45 46// The stack trace function might be invoked very early in the program's 47// execution (e.g. from the very first malloc). 48// As such, we suppress usage of backtrace during this early stage of execution. 49static std::atomic<bool> disable_stacktraces(true); // Disabled until healthy. 50// Waiting until static initializers run seems to be late enough. 51// This file is included into stacktrace.cc so this will only run once. 52ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() { 53 // Check if we can even create stacktraces. If not, bail early and leave 54 // disable_stacktraces set as-is. 55 // clang-format off 56 if (!EM_ASM_INT({ return (typeof wasmOffsetConverter !== 'undefined'); })) { 57 return 0; 58 } 59 // clang-format on 60 disable_stacktraces.store(false, std::memory_order_relaxed); 61 return 0; 62}(); 63 64template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> 65static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, 66 const void *ucp, int *min_dropped_frames) { 67 if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) { 68 return 0; 69 } 70 ++recursive; 71 72 static_cast<void>(ucp); // Unused. 73 constexpr int kStackLength = 64; 74 void *stack[kStackLength]; 75 76 int size; 77 uintptr_t pc = emscripten_stack_snapshot(); 78 size = emscripten_stack_unwind_buffer(pc, stack, kStackLength); 79 80 int result_count = size - skip_count; 81 if (result_count < 0) result_count = 0; 82 if (result_count > max_depth) result_count = max_depth; 83 for (int i = 0; i < result_count; i++) result[i] = stack[i + skip_count]; 84 85 if (IS_STACK_FRAMES) { 86 // No implementation for finding out the stack frame sizes yet. 87 memset(sizes, 0, sizeof(*sizes) * result_count); 88 } 89 if (min_dropped_frames != nullptr) { 90 if (size - skip_count - max_depth > 0) { 91 *min_dropped_frames = size - skip_count - max_depth; 92 } else { 93 *min_dropped_frames = 0; 94 } 95 } 96 97 --recursive; 98 99 return result_count; 100} 101 102namespace absl { 103ABSL_NAMESPACE_BEGIN 104namespace debugging_internal { 105bool StackTraceWorksForTest() { return true; } 106} // namespace debugging_internal 107ABSL_NAMESPACE_END 108} // namespace absl 109 110#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ 111