Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifndef BASE_DEBUG_STACK_TRACE_H_ |
| 6 | #define BASE_DEBUG_STACK_TRACE_H_ |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 7 | |
avi | ebe805c | 2015-12-24 08:20:28 | [diff] [blame] | 8 | #include <stddef.h> |
| 9 | |
Lei Zhang | 4493f55 | 2025-06-14 06:11:31 | [diff] [blame] | 10 | #include <array> |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 11 | #include <iosfwd> |
[email protected] | d4114ba | 2011-10-12 16:13:40 | [diff] [blame] | 12 | #include <string> |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 13 | |
[email protected] | 0bea725 | 2011-08-05 15:34:00 | [diff] [blame] | 14 | #include "base/base_export.h" |
Daniel Cheng | a0e290d | 2023-10-16 18:47:24 | [diff] [blame] | 15 | #include "base/containers/span.h" |
Scott Violet | 4416579 | 2018-02-22 02:08:08 | [diff] [blame] | 16 | #include "base/debug/debugging_buildflags.h" |
Keishi Hattori | f28f4f8 | 2022-06-21 11:32:15 | [diff] [blame] | 17 | #include "base/memory/raw_ptr.h" |
Greg Thompson | c82cbc9 | 2024-04-10 07:36:06 | [diff] [blame] | 18 | #include "base/strings/cstring_view.h" |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 19 | #include "build/build_config.h" |
| 20 | |
Xiaohan Wang | 131aa4d | 2022-01-15 19:39:41 | [diff] [blame] | 21 | #if BUILDFLAG(IS_POSIX) |
Andreas Haas | 54c0c02 | 2019-06-14 17:33:53 | [diff] [blame] | 22 | #include <signal.h> |
[email protected] | 47c4556 | 2012-11-17 14:33:25 | [diff] [blame] | 23 | #include <unistd.h> |
| 24 | #endif |
| 25 | |
Xiaohan Wang | 131aa4d | 2022-01-15 19:39:41 | [diff] [blame] | 26 | #if BUILDFLAG(IS_WIN) |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 27 | struct _EXCEPTION_POINTERS; |
wittman | 7808da7 | 2015-02-03 17:46:51 | [diff] [blame] | 28 | struct _CONTEXT; |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 29 | #endif |
| 30 | |
Jan Keitel | 4161bcb | 2024-08-01 12:44:24 | [diff] [blame] | 31 | namespace base::debug { |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 32 | |
[email protected] | 11b93faa | 2012-11-01 21:58:30 | [diff] [blame] | 33 | // Enables stack dump to console output on exception and signals. |
| 34 | // When enabled, the process will quit immediately. This is meant to be used in |
| 35 | // unit_tests only! This is not thread-safe: only call from main thread. |
jam | 79dc59a | 2015-08-17 03:38:16 | [diff] [blame] | 36 | // In sandboxed processes, this has to be called before the sandbox is turned |
| 37 | // on. |
[email protected] | ad9a012 | 2014-03-22 00:34:52 | [diff] [blame] | 38 | // Calling this function on Linux opens /proc/self/maps and caches its |
jam | 79dc59a | 2015-08-17 03:38:16 | [diff] [blame] | 39 | // contents. In non-official builds, this function also opens the object files |
| 40 | // that are loaded in memory and caches their file descriptors (this cannot be |
[email protected] | ad9a012 | 2014-03-22 00:34:52 | [diff] [blame] | 41 | // done in official builds because it has security implications). |
jam | 79dc59a | 2015-08-17 03:38:16 | [diff] [blame] | 42 | BASE_EXPORT bool EnableInProcessStackDumping(); |
[email protected] | ad9a012 | 2014-03-22 00:34:52 | [diff] [blame] | 43 | |
Nico Weber | 6f2d26d | 2025-06-27 07:32:08 | [diff] [blame] | 44 | #if BUILDFLAG(IS_POSIX) |
Andreas Haas | ef19d59 | 2019-04-30 18:16:51 | [diff] [blame] | 45 | // Sets a first-chance callback for the stack dump signal handler. This callback |
| 46 | // is called at the beginning of the signal handler to handle special kinds of |
| 47 | // signals, like out-of-bounds memory accesses in WebAssembly (WebAssembly Trap |
| 48 | // Handler). |
| 49 | // {SetStackDumpFirstChanceCallback} returns {true} if the callback |
| 50 | // has been set correctly. It returns {false} if the stack dump signal handler |
| 51 | // has not been registered with the OS, e.g. because of ASAN. |
| 52 | BASE_EXPORT bool SetStackDumpFirstChanceCallback(bool (*handler)(int, |
Andreas Haas | 54c0c02 | 2019-06-14 17:33:53 | [diff] [blame] | 53 | siginfo_t*, |
Eric Holk | dc499db | 2017-07-17 17:57:35 | [diff] [blame] | 54 | void*)); |
| 55 | #endif |
| 56 | |
erikchen | d6b2b82 | 2017-02-22 21:10:31 | [diff] [blame] | 57 | // Returns end of the stack, or 0 if we couldn't get it. |
erikchen | f7c8a0d | 2017-04-06 21:15:27 | [diff] [blame] | 58 | #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) |
erikchen | d6b2b82 | 2017-02-22 21:10:31 | [diff] [blame] | 59 | BASE_EXPORT uintptr_t GetStackEnd(); |
| 60 | #endif |
| 61 | |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 62 | // A stacktrace can be helpful in debugging. For example, you can include a |
| 63 | // stacktrace member in a object (probably around #ifndef NDEBUG) so that you |
| 64 | // can later see where the given object was created from. |
[email protected] | 0bea725 | 2011-08-05 15:34:00 | [diff] [blame] | 65 | class BASE_EXPORT StackTrace { |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 66 | public: |
Daniel Cheng | 42dd844 | 2024-03-05 22:14:11 | [diff] [blame] | 67 | // LINT.IfChange(max_stack_frames) |
Daniel Cheng | 1e96ff3 | 2023-06-26 20:01:01 | [diff] [blame] | 68 | #if BUILDFLAG(IS_ANDROID) |
Alison Gale | d94ce4f | 2024-04-22 15:20:39 | [diff] [blame] | 69 | // TODO(crbug.com/41437515): Testing indicates that Android has issues |
Daniel Cheng | 1e96ff3 | 2023-06-26 20:01:01 | [diff] [blame] | 70 | // with a larger value here, so leave Android at 62. |
| 71 | static constexpr size_t kMaxTraces = 62; |
| 72 | #else |
| 73 | // For other platforms, use 250. This seems reasonable without |
| 74 | // being huge. |
| 75 | static constexpr size_t kMaxTraces = 250; |
| 76 | #endif |
Daniel Cheng | 42dd844 | 2024-03-05 22:14:11 | [diff] [blame] | 77 | // LINT.ThenChange(dwarf_line_no.cc:max_stack_frames) |
Daniel Cheng | 1e96ff3 | 2023-06-26 20:01:01 | [diff] [blame] | 78 | |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 79 | // Creates a stacktrace from the current location. |
| 80 | StackTrace(); |
| 81 | |
wez | 73f8d7e | 2017-01-29 06:18:13 | [diff] [blame] | 82 | // Creates a stacktrace from the current location, of up to |count| entries. |
| 83 | // |count| will be limited to at most |kMaxTraces|. |
| 84 | explicit StackTrace(size_t count); |
| 85 | |
danakj | e75c6c81 | 2024-07-26 20:37:47 | [diff] [blame] | 86 | // Creates a stacktrace from an existing array of instruction pointers (such |
| 87 | // as returned by Addresses()). Only the first `kMaxTraces` of the span will |
| 88 | // be used. |
| 89 | explicit StackTrace(span<const void* const> trace); |
[email protected] | f01ae981 | 2011-08-30 19:33:04 | [diff] [blame] | 90 | |
Xiaohan Wang | 131aa4d | 2022-01-15 19:39:41 | [diff] [blame] | 91 | #if BUILDFLAG(IS_WIN) |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 92 | // Creates a stacktrace for an exception. |
| 93 | // Note: this function will throw an import not found (StackWalk64) exception |
| 94 | // on system without dbghelp 5.1. |
jam | 79dc59a | 2015-08-17 03:38:16 | [diff] [blame] | 95 | StackTrace(_EXCEPTION_POINTERS* exception_pointers); |
rnk | 0565cdd6 | 2015-10-06 16:48:30 | [diff] [blame] | 96 | StackTrace(const _CONTEXT* context); |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 97 | #endif |
| 98 | |
Gabriel Charette | ae1bb01 | 2021-05-06 19:31:21 | [diff] [blame] | 99 | // Returns true if this current test environment is expected to have |
| 100 | // symbolized frames when printing a stack trace. |
| 101 | static bool WillSymbolizeToStreamForTesting(); |
| 102 | |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 103 | // Copying and assignment are allowed with the default functions. |
| 104 | |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 105 | // Gets an array of instruction pointer values. |*count| will be set to the |
Bruce Dawson | 19e29bb | 2020-01-06 21:20:14 | [diff] [blame] | 106 | // number of elements in the returned array. Addresses()[0] will contain an |
| 107 | // address from the leaf function, and Addresses()[count-1] will contain an |
| 108 | // address from the root function (i.e.; the thread's entry point). |
Daniel Cheng | a0e290d | 2023-10-16 18:47:24 | [diff] [blame] | 109 | span<const void* const> addresses() const { |
Jan Keitel | 4161bcb | 2024-08-01 12:44:24 | [diff] [blame] | 110 | return span(trace_).first(count_); |
Daniel Cheng | a0e290d | 2023-10-16 18:47:24 | [diff] [blame] | 111 | } |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 112 | |
[email protected] | 5ddbf1c | 2013-08-29 01:59:38 | [diff] [blame] | 113 | // Prints the stack trace to stderr. |
| 114 | void Print() const; |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 115 | |
Mason Freed | b9ef2b6 | 2018-09-10 17:17:27 | [diff] [blame] | 116 | // Prints the stack trace to stderr, prepending the given string before |
| 117 | // each output line. |
Greg Thompson | 8b1b295b | 2024-04-10 07:44:14 | [diff] [blame] | 118 | void PrintWithPrefix(cstring_view prefix_string) const; |
Mason Freed | b9ef2b6 | 2018-09-10 17:17:27 | [diff] [blame] | 119 | |
Gabriel Charette | ae1bb01 | 2021-05-06 19:31:21 | [diff] [blame] | 120 | #if !defined(__UCLIBC__) && !defined(_AIX) |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 121 | // Resolves backtrace to symbols and write to stream. |
[email protected] | 501dfc4 | 2011-04-14 16:34:00 | [diff] [blame] | 122 | void OutputToStream(std::ostream* os) const; |
Mason Freed | b9ef2b6 | 2018-09-10 17:17:27 | [diff] [blame] | 123 | // Resolves backtrace to symbols and write to stream, with the provided |
| 124 | // prefix string prepended to each line. |
| 125 | void OutputToStreamWithPrefix(std::ostream* os, |
Greg Thompson | 8b1b295b | 2024-04-10 07:44:14 | [diff] [blame] | 126 | cstring_view prefix_string) const; |
[email protected] | 816e5045 | 2014-04-09 22:29:38 | [diff] [blame] | 127 | #endif |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 128 | |
[email protected] | d4114ba | 2011-10-12 16:13:40 | [diff] [blame] | 129 | // Resolves backtrace to symbols and returns as string. |
| 130 | std::string ToString() const; |
| 131 | |
Mason Freed | b9ef2b6 | 2018-09-10 17:17:27 | [diff] [blame] | 132 | // Resolves backtrace to symbols and returns as string, prepending the |
| 133 | // provided prefix string to each line. |
Greg Thompson | 8b1b295b | 2024-04-10 07:44:14 | [diff] [blame] | 134 | std::string ToStringWithPrefix(cstring_view prefix_string) const; |
Mason Freed | b9ef2b6 | 2018-09-10 17:17:27 | [diff] [blame] | 135 | |
Greg Thompson | c82cbc9 | 2024-04-10 07:36:06 | [diff] [blame] | 136 | // Sets a message to be emitted in place of symbolized stack traces. When |
| 137 | // such a message is provided, collection and symbolization of stack traces |
| 138 | // is suppressed. Suppression is cancelled if `message` is empty. |
| 139 | static void SuppressStackTracesWithMessageForTesting(std::string message); |
| 140 | |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 141 | private: |
Greg Thompson | c82cbc9 | 2024-04-10 07:36:06 | [diff] [blame] | 142 | // Prints `message` with an optional prefix. |
Greg Thompson | 8b1b295b | 2024-04-10 07:44:14 | [diff] [blame] | 143 | static void PrintMessageWithPrefix(cstring_view prefix_string, |
Greg Thompson | c82cbc9 | 2024-04-10 07:36:06 | [diff] [blame] | 144 | cstring_view message); |
| 145 | |
Greg Thompson | 8b1b295b | 2024-04-10 07:44:14 | [diff] [blame] | 146 | void PrintWithPrefixImpl(cstring_view prefix_string) const; |
Greg Thompson | c82cbc9 | 2024-04-10 07:36:06 | [diff] [blame] | 147 | #if !defined(__UCLIBC__) && !defined(_AIX) |
| 148 | void OutputToStreamWithPrefixImpl(std::ostream* os, |
Greg Thompson | 8b1b295b | 2024-04-10 07:44:14 | [diff] [blame] | 149 | cstring_view prefix_string) const; |
Greg Thompson | c82cbc9 | 2024-04-10 07:36:06 | [diff] [blame] | 150 | #endif |
| 151 | |
Greg Thompson | 75724f9 | 2024-03-21 08:31:27 | [diff] [blame] | 152 | // Returns true if generation of symbolized stack traces is to be suppressed. |
| 153 | static bool ShouldSuppressOutput(); |
| 154 | |
Xiaohan Wang | 131aa4d | 2022-01-15 19:39:41 | [diff] [blame] | 155 | #if BUILDFLAG(IS_WIN) |
rnk | 0565cdd6 | 2015-10-06 16:48:30 | [diff] [blame] | 156 | void InitTrace(const _CONTEXT* context_record); |
wittman | 7808da7 | 2015-02-03 17:46:51 | [diff] [blame] | 157 | #endif |
| 158 | |
Jan Keitel | 4161bcb | 2024-08-01 12:44:24 | [diff] [blame] | 159 | std::array<const void*, kMaxTraces> trace_; |
[email protected] | e645c7a | 2012-07-25 21:43:44 | [diff] [blame] | 160 | |
Greg Thompson | 75724f9 | 2024-03-21 08:31:27 | [diff] [blame] | 161 | // The number of valid frames in |trace_|, or 0 if collection was suppressed. |
| 162 | size_t count_ = 0; |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 163 | }; |
| 164 | |
Gabriel Charette | c41da451 | 2019-02-22 02:07:10 | [diff] [blame] | 165 | // Forwards to StackTrace::OutputToStream(). |
| 166 | BASE_EXPORT std::ostream& operator<<(std::ostream& os, const StackTrace& s); |
| 167 | |
Vlad Tsyrklevich | 2788d5996 | 2019-01-23 03:32:45 | [diff] [blame] | 168 | // Record a stack trace with up to |count| frames into |trace|. Returns the |
| 169 | // number of frames read. |
danakj | e75c6c81 | 2024-07-26 20:37:47 | [diff] [blame] | 170 | BASE_EXPORT size_t CollectStackTrace(span<const void*> trace); |
Vlad Tsyrklevich | 2788d5996 | 2019-01-23 03:32:45 | [diff] [blame] | 171 | |
Greg Thompson | 75724f9 | 2024-03-21 08:31:27 | [diff] [blame] | 172 | // A helper for tests that must either override the default suppression of |
| 173 | // symbolized stack traces in death tests, or the default generation of them in |
| 174 | // normal tests. |
| 175 | class BASE_EXPORT OverrideStackTraceOutputForTesting { |
Greg Thompson | a6e88c1 | 2024-03-13 16:37:13 | [diff] [blame] | 176 | public: |
Greg Thompson | 75724f9 | 2024-03-21 08:31:27 | [diff] [blame] | 177 | enum class Mode { |
| 178 | kUnset, |
| 179 | kForceOutput, |
| 180 | kSuppressOutput, |
| 181 | }; |
| 182 | explicit OverrideStackTraceOutputForTesting(Mode mode); |
| 183 | OverrideStackTraceOutputForTesting( |
| 184 | const OverrideStackTraceOutputForTesting&) = delete; |
| 185 | OverrideStackTraceOutputForTesting& operator=( |
| 186 | const OverrideStackTraceOutputForTesting&) = delete; |
| 187 | ~OverrideStackTraceOutputForTesting(); |
Greg Thompson | a6e88c1 | 2024-03-13 16:37:13 | [diff] [blame] | 188 | }; |
| 189 | |
erikchen | f7c8a0d | 2017-04-06 21:15:27 | [diff] [blame] | 190 | #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) |
ssid | c1d5a3d | 2020-09-11 05:20:30 | [diff] [blame] | 191 | |
| 192 | // For stack scanning to be efficient it's very important for the thread to |
| 193 | // be started by Chrome. In that case we naturally terminate unwinding once |
| 194 | // we reach the origin of the stack (i.e. GetStackEnd()). If the thread is |
| 195 | // not started by Chrome (e.g. Android's main thread), then we end up always |
| 196 | // scanning area at the origin of the stack, wasting time and not finding any |
| 197 | // frames (since Android libraries don't have frame pointers). Scanning is not |
| 198 | // enabled on other posix platforms due to legacy reasons. |
Xiaohan Wang | 131aa4d | 2022-01-15 19:39:41 | [diff] [blame] | 199 | #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
ssid | c1d5a3d | 2020-09-11 05:20:30 | [diff] [blame] | 200 | constexpr bool kEnableScanningByDefault = true; |
| 201 | #else |
| 202 | constexpr bool kEnableScanningByDefault = false; |
| 203 | #endif |
| 204 | |
dskiba | 79080da0 | 2016-04-23 00:10:27 | [diff] [blame] | 205 | // Traces the stack by using frame pointers. This function is faster but less |
| 206 | // reliable than StackTrace. It should work for debug and profiling builds, |
| 207 | // but not for release builds (although there are some exceptions). |
| 208 | // |
| 209 | // Writes at most |max_depth| frames (instruction pointers) into |out_trace| |
| 210 | // after skipping |skip_initial| frames. Note that the function itself is not |
| 211 | // added to the trace so |skip_initial| should be 0 in most cases. |
ssid | c1d5a3d | 2020-09-11 05:20:30 | [diff] [blame] | 212 | // Returns number of frames written. |enable_scanning| enables scanning on |
| 213 | // platforms that do not enable scanning by default. |
| 214 | BASE_EXPORT size_t |
danakj | e75c6c81 | 2024-07-26 20:37:47 | [diff] [blame] | 215 | TraceStackFramePointers(span<const void*> out_trace, |
ssid | c1d5a3d | 2020-09-11 05:20:30 | [diff] [blame] | 216 | size_t skip_initial, |
| 217 | bool enable_scanning = kEnableScanningByDefault); |
| 218 | |
dskiba | a8c951e | 2016-10-25 23:39:11 | [diff] [blame] | 219 | // Links stack frame |fp| to |parent_fp|, so that during stack unwinding |
| 220 | // TraceStackFramePointers() visits |parent_fp| after visiting |fp|. |
| 221 | // Both frame pointers must come from __builtin_frame_address(). |
| 222 | // Destructor restores original linkage of |fp| to avoid corrupting caller's |
| 223 | // frame register on return. |
| 224 | // |
| 225 | // This class can be used to repair broken stack frame chain in cases |
| 226 | // when execution flow goes into code built without frame pointers: |
| 227 | // |
| 228 | // void DoWork() { |
| 229 | // Call_SomeLibrary(); |
| 230 | // } |
| 231 | // static __thread void* g_saved_fp; |
| 232 | // void Call_SomeLibrary() { |
| 233 | // g_saved_fp = __builtin_frame_address(0); |
| 234 | // some_library_call(...); // indirectly calls SomeLibrary_Callback() |
| 235 | // } |
| 236 | // void SomeLibrary_Callback() { |
| 237 | // ScopedStackFrameLinker linker(__builtin_frame_address(0), g_saved_fp); |
| 238 | // ... |
| 239 | // TraceStackFramePointers(...); |
| 240 | // } |
| 241 | // |
| 242 | // This produces the following trace: |
| 243 | // |
| 244 | // #0 SomeLibrary_Callback() |
| 245 | // #1 <address of the code inside SomeLibrary that called #0> |
| 246 | // #2 DoWork() |
| 247 | // ...rest of the trace... |
| 248 | // |
| 249 | // SomeLibrary doesn't use frame pointers, so when SomeLibrary_Callback() |
| 250 | // is called, stack frame register contains bogus value that becomes callback' |
| 251 | // parent frame address. Without ScopedStackFrameLinker unwinding would've |
| 252 | // stopped at that bogus frame address yielding just two first frames (#0, #1). |
| 253 | // ScopedStackFrameLinker overwrites callback's parent frame address with |
| 254 | // Call_SomeLibrary's frame, so unwinder produces full trace without even |
| 255 | // noticing that stack frame chain was broken. |
| 256 | class BASE_EXPORT ScopedStackFrameLinker { |
| 257 | public: |
| 258 | ScopedStackFrameLinker(void* fp, void* parent_fp); |
Peter Boström | 7319bbd | 2021-09-15 22:59:38 | [diff] [blame] | 259 | |
| 260 | ScopedStackFrameLinker(const ScopedStackFrameLinker&) = delete; |
| 261 | ScopedStackFrameLinker& operator=(const ScopedStackFrameLinker&) = delete; |
| 262 | |
dskiba | a8c951e | 2016-10-25 23:39:11 | [diff] [blame] | 263 | ~ScopedStackFrameLinker(); |
| 264 | |
| 265 | private: |
Keishi Hattori | f28f4f8 | 2022-06-21 11:32:15 | [diff] [blame] | 266 | raw_ptr<void> fp_; |
| 267 | raw_ptr<void> parent_fp_; |
| 268 | raw_ptr<void> original_parent_fp_; |
dskiba | a8c951e | 2016-10-25 23:39:11 | [diff] [blame] | 269 | }; |
| 270 | |
erikchen | f7c8a0d | 2017-04-06 21:15:27 | [diff] [blame] | 271 | #endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) |
dskiba | 79080da0 | 2016-04-23 00:10:27 | [diff] [blame] | 272 | |
[email protected] | 1e218b7 | 2012-11-14 19:32:23 | [diff] [blame] | 273 | namespace internal { |
| 274 | |
Xiaohan Wang | 131aa4d | 2022-01-15 19:39:41 | [diff] [blame] | 275 | #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) |
[email protected] | 1e218b7 | 2012-11-14 19:32:23 | [diff] [blame] | 276 | // POSIX doesn't define any async-signal safe function for converting |
| 277 | // an integer to ASCII. We'll have to define our own version. |
| 278 | // itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the |
| 279 | // conversion was successful or NULL otherwise. It never writes more than "sz" |
| 280 | // bytes. Output will be truncated as needed, and a NUL character is always |
| 281 | // appended. |
danakj | d53babfe | 2024-10-09 14:04:16 | [diff] [blame] | 282 | BASE_EXPORT void itoa_r(intptr_t i, |
| 283 | int base, |
| 284 | size_t padding, |
| 285 | base::span<char> buf); |
Xiaohan Wang | 131aa4d | 2022-01-15 19:39:41 | [diff] [blame] | 286 | #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) |
[email protected] | 1e218b7 | 2012-11-14 19:32:23 | [diff] [blame] | 287 | |
| 288 | } // namespace internal |
| 289 | |
Jan Keitel | 4161bcb | 2024-08-01 12:44:24 | [diff] [blame] | 290 | } // namespace base::debug |
[email protected] | 5858035 | 2010-10-26 04:07:50 | [diff] [blame] | 291 | |
| 292 | #endif // BASE_DEBUG_STACK_TRACE_H_ |