| // Copyright 2018 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "base/debug/stack_trace.h" |
| |
| #include <algorithm> |
| #include <ostream> |
| |
| #include "starboard/common/log.h" |
| #include "starboard/system.h" |
| |
| namespace base { |
| namespace debug { |
| |
| namespace { |
| |
| class BacktraceOutputHandler { |
| public: |
| virtual void HandleOutput(const char* output) = 0; |
| |
| protected: |
| virtual ~BacktraceOutputHandler() {} |
| }; |
| |
| class PrintBacktraceOutputHandler : public BacktraceOutputHandler { |
| public: |
| PrintBacktraceOutputHandler() {} |
| |
| virtual void HandleOutput(const char* output) { |
| // NOTE: This code MUST be async-signal safe (it's used by in-process |
| // stack dumping signal handler). NO malloc or stdio is allowed here. |
| SbLogRaw(output); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(PrintBacktraceOutputHandler); |
| }; |
| |
| class StreamBacktraceOutputHandler : public BacktraceOutputHandler { |
| public: |
| StreamBacktraceOutputHandler(std::ostream* os) : os_(os) {} |
| |
| virtual void HandleOutput(const char* output) { (*os_) << output; } |
| |
| private: |
| std::ostream* os_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StreamBacktraceOutputHandler); |
| }; |
| |
| // NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. |
| char* itoa_r(intptr_t i, char* buf, size_t sz, int base) { |
| // Make sure we can write at least one NUL byte. |
| size_t n = 1; |
| if (n > sz) |
| return NULL; |
| |
| if (base < 2 || base > 16) { |
| buf[0] = '\000'; |
| return NULL; |
| } |
| |
| char* start = buf; |
| |
| uintptr_t j = i; |
| |
| // Handle negative numbers (only for base 10). |
| if (i < 0 && base == 10) { |
| j = -i; |
| |
| // Make sure we can write the '-' character. |
| if (++n > sz) { |
| buf[0] = '\000'; |
| return NULL; |
| } |
| *start++ = '-'; |
| } |
| |
| // Loop until we have converted the entire number. Output at least one |
| // character (i.e. '0'). |
| char* ptr = start; |
| do { |
| // Make sure there is still enough space left in our output buffer. |
| if (++n > sz) { |
| buf[0] = '\000'; |
| return NULL; |
| } |
| |
| // Output the next digit. |
| *ptr++ = "0123456789abcdef"[j % base]; |
| j /= base; |
| } while (j); |
| |
| // Terminate the output with a NUL character. |
| *ptr = '\000'; |
| |
| // Conversion to ASCII actually resulted in the digits being in reverse |
| // order. We can't easily generate them in forward order, as we can't tell |
| // the number of characters needed until we are done converting. |
| // So, now, we reverse the string (except for the possible "-" sign). |
| while (--ptr > start) { |
| char ch = *ptr; |
| *ptr = *start; |
| *start++ = ch; |
| } |
| return buf; |
| } |
| |
| void OutputPointer(void* pointer, BacktraceOutputHandler* handler) { |
| char buf[1024] = {'\0'}; |
| handler->HandleOutput(" [0x"); |
| itoa_r(reinterpret_cast<intptr_t>(pointer), buf, sizeof(buf), 16); |
| handler->HandleOutput(buf); |
| handler->HandleOutput("]"); |
| } |
| |
| void ProcessBacktrace(void* const* trace, |
| int size, |
| const char* prefix_string, |
| BacktraceOutputHandler* handler) { |
| for (int i = 0; i < size; ++i) { |
| if (prefix_string) |
| handler->HandleOutput(prefix_string); |
| handler->HandleOutput("\t"); |
| |
| char buf[1024] = {'\0'}; |
| |
| // Subtract by one as return address of function may be in the next |
| // function when a function is annotated as noreturn. |
| void* address = static_cast<char*>(trace[i]) - 1; |
| if (SbSystemSymbolize(address, buf, sizeof(buf))) { |
| handler->HandleOutput(buf); |
| } else { |
| handler->HandleOutput("<unknown>"); |
| } |
| |
| OutputPointer(trace[i], handler); |
| handler->HandleOutput("\n"); |
| } |
| } |
| |
| } // namespace |
| |
| StackTrace::StackTrace(size_t count) { |
| // NOTE: This code MUST be async-signal safe (it's used by in-process |
| // stack dumping signal handler). NO malloc or stdio is allowed here. |
| |
| // Though the SbSystemGetStack API documentation does not specify any possible |
| // negative return values, we take no chance. |
| count_ = std::max(SbSystemGetStack(trace_, count), 0); |
| if (count_ < 1) { |
| return; |
| } |
| |
| // We can remove this call from the stack trace, since we know it is always |
| // going to be in it. |
| for (int i = 1; i < count_; ++i) { |
| trace_[i - 1] = trace_[i]; |
| } |
| } |
| |
| void StackTrace::PrintWithPrefix(const char* prefix_string) const { |
| // NOTE: This code MUST be async-signal safe (it's used by in-process |
| // stack dumping signal handler). NO malloc or stdio is allowed here. |
| PrintBacktraceOutputHandler handler; |
| ProcessBacktrace(trace_, count_, prefix_string, &handler); |
| } |
| |
| void StackTrace::OutputToStreamWithPrefix(std::ostream* os, |
| const char* prefix_string) const { |
| StreamBacktraceOutputHandler handler(os); |
| ProcessBacktrace(trace_, count_, prefix_string, &handler); |
| } |
| |
| bool EnableInProcessStackDumping() { |
| return true; |
| } |
| |
| } // namespace debug |
| } // namespace base |