| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/debug/stack_trace.h" | 5 #include "base/debug/stack_trace.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <execinfo.h> | 8 #include <execinfo.h> |
| 9 #include <fcntl.h> | 9 #include <fcntl.h> |
| 10 #include <signal.h> | 10 #include <signal.h> |
| 11 #include <stdio.h> | 11 #include <stdio.h> |
| 12 #include <stdlib.h> | 12 #include <stdlib.h> |
| 13 #include <sys/param.h> | 13 #include <sys/param.h> |
| 14 #include <sys/stat.h> | 14 #include <sys/stat.h> |
| 15 #include <sys/types.h> | 15 #include <sys/types.h> |
| 16 #include <unistd.h> | 16 #include <unistd.h> |
| 17 | 17 |
| 18 #include <ostream> | 18 #include <ostream> |
| 19 | 19 |
| 20 #if defined(__GLIBCXX__) | 20 #if defined(__GLIBCXX__) |
| 21 #include <cxxabi.h> | 21 #include <cxxabi.h> |
| 22 #endif | 22 #endif |
| 23 | 23 |
| 24 #if defined(OS_MACOSX) | 24 #if defined(OS_MACOSX) |
| 25 #include <AvailabilityMacros.h> | 25 #include <AvailabilityMacros.h> |
| 26 #endif | 26 #endif |
| 27 | 27 |
| 28 #include "base/basictypes.h" | 28 #include "base/basictypes.h" |
| 29 #include "base/debug/debugger.h" | 29 #include "base/debug/debugger.h" |
| 30 #include "base/debug/format.h" |
| 30 #include "base/logging.h" | 31 #include "base/logging.h" |
| 31 #include "base/memory/scoped_ptr.h" | 32 #include "base/memory/scoped_ptr.h" |
| 32 #include "base/posix/eintr_wrapper.h" | 33 #include "base/posix/eintr_wrapper.h" |
| 33 #include "base/strings/string_number_conversions.h" | 34 #include "base/strings/string_number_conversions.h" |
| 34 | 35 |
| 35 #if defined(USE_SYMBOLIZE) | 36 #if defined(USE_SYMBOLIZE) |
| 36 #include "base/third_party/symbolize/symbolize.h" | 37 #include "base/third_party/symbolize/symbolize.h" |
| 37 #endif | 38 #endif |
| 38 | 39 |
| 39 namespace base { | 40 namespace base { |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 | 106 |
| 106 class BacktraceOutputHandler { | 107 class BacktraceOutputHandler { |
| 107 public: | 108 public: |
| 108 virtual void HandleOutput(const char* output) = 0; | 109 virtual void HandleOutput(const char* output) = 0; |
| 109 | 110 |
| 110 protected: | 111 protected: |
| 111 virtual ~BacktraceOutputHandler() {} | 112 virtual ~BacktraceOutputHandler() {} |
| 112 }; | 113 }; |
| 113 | 114 |
| 114 void OutputPointer(void* pointer, BacktraceOutputHandler* handler) { | 115 void OutputPointer(void* pointer, BacktraceOutputHandler* handler) { |
| 115 char buf[1024] = { '\0' }; | 116 char buf[30]; |
| 116 handler->HandleOutput(" [0x"); | 117 Format(buf, " [0x%12x]", reinterpret_cast<intptr_t>(pointer)); |
| 117 internal::itoa_r(reinterpret_cast<intptr_t>(pointer), | |
| 118 buf, sizeof(buf), 16, 12); | |
| 119 handler->HandleOutput(buf); | 118 handler->HandleOutput(buf); |
| 120 handler->HandleOutput("]"); | |
| 121 } | 119 } |
| 122 | 120 |
| 123 void ProcessBacktrace(void *const *trace, | 121 void ProcessBacktrace(void *const *trace, |
| 124 int size, | 122 int size, |
| 125 BacktraceOutputHandler* handler) { | 123 BacktraceOutputHandler* handler) { |
| 126 // NOTE: This code MUST be async-signal safe (it's used by in-process | 124 // NOTE: This code MUST be async-signal safe (it's used by in-process |
| 127 // stack dumping signal handler). NO malloc or stdio is allowed here. | 125 // stack dumping signal handler). NO malloc or stdio is allowed here. |
| 128 | 126 |
| 129 #if defined(USE_SYMBOLIZE) | 127 #if defined(USE_SYMBOLIZE) |
| 130 for (int i = 0; i < size; ++i) { | 128 for (int i = 0; i < size; ++i) { |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 181 // NOTE: This code MUST be async-signal safe. | 179 // NOTE: This code MUST be async-signal safe. |
| 182 // NO malloc or stdio is allowed here. | 180 // NO malloc or stdio is allowed here. |
| 183 | 181 |
| 184 // Record the fact that we are in the signal handler now, so that the rest | 182 // Record the fact that we are in the signal handler now, so that the rest |
| 185 // of StackTrace can behave in an async-signal-safe manner. | 183 // of StackTrace can behave in an async-signal-safe manner. |
| 186 in_signal_handler = 1; | 184 in_signal_handler = 1; |
| 187 | 185 |
| 188 if (BeingDebugged()) | 186 if (BeingDebugged()) |
| 189 BreakDebugger(); | 187 BreakDebugger(); |
| 190 | 188 |
| 191 PrintToStderr("Received signal "); | 189 char buf[80]; |
| 192 char buf[1024] = { 0 }; | 190 Format(buf, "Received signal %d ", signal); |
| 193 internal::itoa_r(signal, buf, sizeof(buf), 10, 0); | |
| 194 PrintToStderr(buf); | 191 PrintToStderr(buf); |
| 195 if (signal == SIGBUS) { | 192 if (signal == SIGBUS) { |
| 196 if (info->si_code == BUS_ADRALN) | 193 if (info->si_code == BUS_ADRALN) |
| 197 PrintToStderr(" BUS_ADRALN "); | 194 PrintToStderr("BUS_ADRALN "); |
| 198 else if (info->si_code == BUS_ADRERR) | 195 else if (info->si_code == BUS_ADRERR) |
| 199 PrintToStderr(" BUS_ADRERR "); | 196 PrintToStderr("BUS_ADRERR "); |
| 200 else if (info->si_code == BUS_OBJERR) | 197 else if (info->si_code == BUS_OBJERR) |
| 201 PrintToStderr(" BUS_OBJERR "); | 198 PrintToStderr("BUS_OBJERR "); |
| 202 else | 199 else |
| 203 PrintToStderr(" <unknown> "); | 200 PrintToStderr("<unknown> "); |
| 204 } else if (signal == SIGFPE) { | 201 } else if (signal == SIGFPE) { |
| 205 if (info->si_code == FPE_FLTDIV) | 202 if (info->si_code == FPE_FLTDIV) |
| 206 PrintToStderr(" FPE_FLTDIV "); | 203 PrintToStderr("FPE_FLTDIV "); |
| 207 else if (info->si_code == FPE_FLTINV) | 204 else if (info->si_code == FPE_FLTINV) |
| 208 PrintToStderr(" FPE_FLTINV "); | 205 PrintToStderr("FPE_FLTINV "); |
| 209 else if (info->si_code == FPE_FLTOVF) | 206 else if (info->si_code == FPE_FLTOVF) |
| 210 PrintToStderr(" FPE_FLTOVF "); | 207 PrintToStderr("FPE_FLTOVF "); |
| 211 else if (info->si_code == FPE_FLTRES) | 208 else if (info->si_code == FPE_FLTRES) |
| 212 PrintToStderr(" FPE_FLTRES "); | 209 PrintToStderr("FPE_FLTRES "); |
| 213 else if (info->si_code == FPE_FLTSUB) | 210 else if (info->si_code == FPE_FLTSUB) |
| 214 PrintToStderr(" FPE_FLTSUB "); | 211 PrintToStderr("FPE_FLTSUB "); |
| 215 else if (info->si_code == FPE_FLTUND) | 212 else if (info->si_code == FPE_FLTUND) |
| 216 PrintToStderr(" FPE_FLTUND "); | 213 PrintToStderr("FPE_FLTUND "); |
| 217 else if (info->si_code == FPE_INTDIV) | 214 else if (info->si_code == FPE_INTDIV) |
| 218 PrintToStderr(" FPE_INTDIV "); | 215 PrintToStderr("FPE_INTDIV "); |
| 219 else if (info->si_code == FPE_INTOVF) | 216 else if (info->si_code == FPE_INTOVF) |
| 220 PrintToStderr(" FPE_INTOVF "); | 217 PrintToStderr("FPE_INTOVF "); |
| 221 else | 218 else |
| 222 PrintToStderr(" <unknown> "); | 219 PrintToStderr("<unknown> "); |
| 223 } else if (signal == SIGILL) { | 220 } else if (signal == SIGILL) { |
| 224 if (info->si_code == ILL_BADSTK) | 221 if (info->si_code == ILL_BADSTK) |
| 225 PrintToStderr(" ILL_BADSTK "); | 222 PrintToStderr("ILL_BADSTK "); |
| 226 else if (info->si_code == ILL_COPROC) | 223 else if (info->si_code == ILL_COPROC) |
| 227 PrintToStderr(" ILL_COPROC "); | 224 PrintToStderr("ILL_COPROC "); |
| 228 else if (info->si_code == ILL_ILLOPN) | 225 else if (info->si_code == ILL_ILLOPN) |
| 229 PrintToStderr(" ILL_ILLOPN "); | 226 PrintToStderr("ILL_ILLOPN "); |
| 230 else if (info->si_code == ILL_ILLADR) | 227 else if (info->si_code == ILL_ILLADR) |
| 231 PrintToStderr(" ILL_ILLADR "); | 228 PrintToStderr("ILL_ILLADR "); |
| 232 else if (info->si_code == ILL_ILLTRP) | 229 else if (info->si_code == ILL_ILLTRP) |
| 233 PrintToStderr(" ILL_ILLTRP "); | 230 PrintToStderr("ILL_ILLTRP "); |
| 234 else if (info->si_code == ILL_PRVOPC) | 231 else if (info->si_code == ILL_PRVOPC) |
| 235 PrintToStderr(" ILL_PRVOPC "); | 232 PrintToStderr("ILL_PRVOPC "); |
| 236 else if (info->si_code == ILL_PRVREG) | 233 else if (info->si_code == ILL_PRVREG) |
| 237 PrintToStderr(" ILL_PRVREG "); | 234 PrintToStderr("ILL_PRVREG "); |
| 238 else | 235 else |
| 239 PrintToStderr(" <unknown> "); | 236 PrintToStderr("<unknown> "); |
| 240 } else if (signal == SIGSEGV) { | 237 } else if (signal == SIGSEGV) { |
| 241 if (info->si_code == SEGV_MAPERR) | 238 if (info->si_code == SEGV_MAPERR) |
| 242 PrintToStderr(" SEGV_MAPERR "); | 239 PrintToStderr("SEGV_MAPERR "); |
| 243 else if (info->si_code == SEGV_ACCERR) | 240 else if (info->si_code == SEGV_ACCERR) |
| 244 PrintToStderr(" SEGV_ACCERR "); | 241 PrintToStderr("SEGV_ACCERR "); |
| 245 else | 242 else |
| 246 PrintToStderr(" <unknown> "); | 243 PrintToStderr("<unknown> "); |
| 247 } | 244 } |
| 248 if (signal == SIGBUS || signal == SIGFPE || | 245 if (signal == SIGBUS || signal == SIGFPE || |
| 249 signal == SIGILL || signal == SIGSEGV) { | 246 signal == SIGILL || signal == SIGSEGV) { |
| 250 internal::itoa_r(reinterpret_cast<intptr_t>(info->si_addr), | 247 Format(buf, "%12x", reinterpret_cast<intptr_t>(info->si_addr)); |
| 251 buf, sizeof(buf), 16, 12); | |
| 252 PrintToStderr(buf); | 248 PrintToStderr(buf); |
| 253 } | 249 } |
| 254 PrintToStderr("\n"); | 250 PrintToStderr("\n"); |
| 255 | 251 |
| 256 debug::StackTrace().PrintBacktrace(); | 252 debug::StackTrace().PrintBacktrace(); |
| 257 | 253 |
| 258 #if defined(OS_LINUX) | 254 #if defined(OS_LINUX) |
| 259 #if ARCH_CPU_X86_FAMILY | 255 #if ARCH_CPU_X86_FAMILY |
| 260 ucontext_t* context = reinterpret_cast<ucontext_t*>(void_context); | 256 ucontext_t* context = reinterpret_cast<ucontext_t*>(void_context); |
| 261 const struct { | 257 const struct { |
| 262 const char* label; | 258 const char* label; |
| 263 greg_t value; | 259 greg_t value; |
| 264 } registers[] = { | 260 } registers[] = { |
| 265 #if ARCH_CPU_32_BITS | 261 #if ARCH_CPU_32_BITS |
| 266 { " gs: ", context->uc_mcontext.gregs[REG_GS] }, | 262 { "gs", context->uc_mcontext.gregs[REG_GS] }, |
| 267 { " fs: ", context->uc_mcontext.gregs[REG_FS] }, | 263 { "fs", context->uc_mcontext.gregs[REG_FS] }, |
| 268 { " es: ", context->uc_mcontext.gregs[REG_ES] }, | 264 { "es", context->uc_mcontext.gregs[REG_ES] }, |
| 269 { " ds: ", context->uc_mcontext.gregs[REG_DS] }, | 265 { "ds", context->uc_mcontext.gregs[REG_DS] }, |
| 270 { " edi: ", context->uc_mcontext.gregs[REG_EDI] }, | 266 { "edi", context->uc_mcontext.gregs[REG_EDI] }, |
| 271 { " esi: ", context->uc_mcontext.gregs[REG_ESI] }, | 267 { "esi", context->uc_mcontext.gregs[REG_ESI] }, |
| 272 { " ebp: ", context->uc_mcontext.gregs[REG_EBP] }, | 268 { "ebp", context->uc_mcontext.gregs[REG_EBP] }, |
| 273 { " esp: ", context->uc_mcontext.gregs[REG_ESP] }, | 269 { "esp", context->uc_mcontext.gregs[REG_ESP] }, |
| 274 { " ebx: ", context->uc_mcontext.gregs[REG_EBX] }, | 270 { "ebx", context->uc_mcontext.gregs[REG_EBX] }, |
| 275 { " edx: ", context->uc_mcontext.gregs[REG_EDX] }, | 271 { "edx", context->uc_mcontext.gregs[REG_EDX] }, |
| 276 { " ecx: ", context->uc_mcontext.gregs[REG_ECX] }, | 272 { "ecx", context->uc_mcontext.gregs[REG_ECX] }, |
| 277 { " eax: ", context->uc_mcontext.gregs[REG_EAX] }, | 273 { "eax", context->uc_mcontext.gregs[REG_EAX] }, |
| 278 { " trp: ", context->uc_mcontext.gregs[REG_TRAPNO] }, | 274 { "trp", context->uc_mcontext.gregs[REG_TRAPNO] }, |
| 279 { " err: ", context->uc_mcontext.gregs[REG_ERR] }, | 275 { "err", context->uc_mcontext.gregs[REG_ERR] }, |
| 280 { " ip: ", context->uc_mcontext.gregs[REG_EIP] }, | 276 { "ip", context->uc_mcontext.gregs[REG_EIP] }, |
| 281 { " cs: ", context->uc_mcontext.gregs[REG_CS] }, | 277 { "cs", context->uc_mcontext.gregs[REG_CS] }, |
| 282 { " efl: ", context->uc_mcontext.gregs[REG_EFL] }, | 278 { "efl", context->uc_mcontext.gregs[REG_EFL] }, |
| 283 { " usp: ", context->uc_mcontext.gregs[REG_UESP] }, | 279 { "usp", context->uc_mcontext.gregs[REG_UESP] }, |
| 284 { " ss: ", context->uc_mcontext.gregs[REG_SS] }, | 280 { "ss", context->uc_mcontext.gregs[REG_SS] }, |
| 285 #elif ARCH_CPU_64_BITS | 281 #elif ARCH_CPU_64_BITS |
| 286 { " r8: ", context->uc_mcontext.gregs[REG_R8] }, | 282 { "r8", context->uc_mcontext.gregs[REG_R8] }, |
| 287 { " r9: ", context->uc_mcontext.gregs[REG_R9] }, | 283 { "r9", context->uc_mcontext.gregs[REG_R9] }, |
| 288 { " r10: ", context->uc_mcontext.gregs[REG_R10] }, | 284 { "r10", context->uc_mcontext.gregs[REG_R10] }, |
| 289 { " r11: ", context->uc_mcontext.gregs[REG_R11] }, | 285 { "r11", context->uc_mcontext.gregs[REG_R11] }, |
| 290 { " r12: ", context->uc_mcontext.gregs[REG_R12] }, | 286 { "r12", context->uc_mcontext.gregs[REG_R12] }, |
| 291 { " r13: ", context->uc_mcontext.gregs[REG_R13] }, | 287 { "r13", context->uc_mcontext.gregs[REG_R13] }, |
| 292 { " r14: ", context->uc_mcontext.gregs[REG_R14] }, | 288 { "r14", context->uc_mcontext.gregs[REG_R14] }, |
| 293 { " r15: ", context->uc_mcontext.gregs[REG_R15] }, | 289 { "r15", context->uc_mcontext.gregs[REG_R15] }, |
| 294 { " di: ", context->uc_mcontext.gregs[REG_RDI] }, | 290 { "di", context->uc_mcontext.gregs[REG_RDI] }, |
| 295 { " si: ", context->uc_mcontext.gregs[REG_RSI] }, | 291 { "si", context->uc_mcontext.gregs[REG_RSI] }, |
| 296 { " bp: ", context->uc_mcontext.gregs[REG_RBP] }, | 292 { "bp", context->uc_mcontext.gregs[REG_RBP] }, |
| 297 { " bx: ", context->uc_mcontext.gregs[REG_RBX] }, | 293 { "bx", context->uc_mcontext.gregs[REG_RBX] }, |
| 298 { " dx: ", context->uc_mcontext.gregs[REG_RDX] }, | 294 { "dx", context->uc_mcontext.gregs[REG_RDX] }, |
| 299 { " ax: ", context->uc_mcontext.gregs[REG_RAX] }, | 295 { "ax", context->uc_mcontext.gregs[REG_RAX] }, |
| 300 { " cx: ", context->uc_mcontext.gregs[REG_RCX] }, | 296 { "cx", context->uc_mcontext.gregs[REG_RCX] }, |
| 301 { " sp: ", context->uc_mcontext.gregs[REG_RSP] }, | 297 { "sp", context->uc_mcontext.gregs[REG_RSP] }, |
| 302 { " ip: ", context->uc_mcontext.gregs[REG_RIP] }, | 298 { "ip", context->uc_mcontext.gregs[REG_RIP] }, |
| 303 { " efl: ", context->uc_mcontext.gregs[REG_EFL] }, | 299 { "efl", context->uc_mcontext.gregs[REG_EFL] }, |
| 304 { " cgf: ", context->uc_mcontext.gregs[REG_CSGSFS] }, | 300 { "cgf", context->uc_mcontext.gregs[REG_CSGSFS] }, |
| 305 { " erf: ", context->uc_mcontext.gregs[REG_ERR] }, | 301 { "erf", context->uc_mcontext.gregs[REG_ERR] }, |
| 306 { " trp: ", context->uc_mcontext.gregs[REG_TRAPNO] }, | 302 { "trp", context->uc_mcontext.gregs[REG_TRAPNO] }, |
| 307 { " msk: ", context->uc_mcontext.gregs[REG_OLDMASK] }, | 303 { "msk", context->uc_mcontext.gregs[REG_OLDMASK] }, |
| 308 { " cr2: ", context->uc_mcontext.gregs[REG_CR2] }, | 304 { "cr2", context->uc_mcontext.gregs[REG_CR2] }, |
| 309 #endif | 305 #endif |
| 310 }; | 306 }; |
| 311 | 307 |
| 312 #if ARCH_CPU_32_BITS | 308 #if ARCH_CPU_32_BITS |
| 313 const int kRegisterPadding = 8; | 309 #define K_REGISTER_PADDING "8" |
| 314 #elif ARCH_CPU_64_BITS | 310 #elif ARCH_CPU_64_BITS |
| 315 const int kRegisterPadding = 16; | 311 #define K_REGISTER_PADDING "16" |
| 316 #endif | 312 #endif |
| 317 | 313 |
| 318 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(registers); i++) { | 314 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(registers); i++) { |
| 319 PrintToStderr(registers[i].label); | 315 Format(buf, "%4s: %"K_REGISTER_PADDING"x%s", |
| 320 internal::itoa_r(registers[i].value, buf, sizeof(buf), | 316 registers[i].label, |
| 321 16, kRegisterPadding); | 317 registers[i].value, |
| 318 (i + 1) % 4 == 0 ? "\n" : ""); |
| 322 PrintToStderr(buf); | 319 PrintToStderr(buf); |
| 323 | |
| 324 if ((i + 1) % 4 == 0) | |
| 325 PrintToStderr("\n"); | |
| 326 } | 320 } |
| 327 PrintToStderr("\n"); | 321 PrintToStderr("\n"); |
| 328 #endif | 322 #endif |
| 329 #elif defined(OS_MACOSX) | 323 #elif defined(OS_MACOSX) |
| 330 // TODO(shess): Port to 64-bit. | 324 // TODO(shess): Port to 64-bit. |
| 331 #if ARCH_CPU_X86_FAMILY && ARCH_CPU_32_BITS | 325 #if ARCH_CPU_X86_FAMILY && ARCH_CPU_32_BITS |
| 332 ucontext_t* context = reinterpret_cast<ucontext_t*>(void_context); | 326 ucontext_t* context = reinterpret_cast<ucontext_t*>(void_context); |
| 333 size_t len; | 327 size_t len; |
| 334 | 328 |
| 335 // NOTE: Even |snprintf()| is not on the approved list for signal | 329 // NOTE: Even |snprintf()| is not on the approved list for signal |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 480 | 474 |
| 481 PrintBacktraceOutputHandler handler; | 475 PrintBacktraceOutputHandler handler; |
| 482 ProcessBacktrace(trace_, count_, &handler); | 476 ProcessBacktrace(trace_, count_, &handler); |
| 483 } | 477 } |
| 484 | 478 |
| 485 void StackTrace::OutputToStream(std::ostream* os) const { | 479 void StackTrace::OutputToStream(std::ostream* os) const { |
| 486 StreamBacktraceOutputHandler handler(os); | 480 StreamBacktraceOutputHandler handler(os); |
| 487 ProcessBacktrace(trace_, count_, &handler); | 481 ProcessBacktrace(trace_, count_, &handler); |
| 488 } | 482 } |
| 489 | 483 |
| 490 namespace internal { | |
| 491 | |
| 492 // NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. | |
| 493 char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) { | |
| 494 // Make sure we can write at least one NUL byte. | |
| 495 size_t n = 1; | |
| 496 if (n > sz) | |
| 497 return NULL; | |
| 498 | |
| 499 if (base < 2 || base > 16) { | |
| 500 buf[0] = '\000'; | |
| 501 return NULL; | |
| 502 } | |
| 503 | |
| 504 char *start = buf; | |
| 505 | |
| 506 uintptr_t j = i; | |
| 507 | |
| 508 // Handle negative numbers (only for base 10). | |
| 509 if (i < 0 && base == 10) { | |
| 510 j = -i; | |
| 511 | |
| 512 // Make sure we can write the '-' character. | |
| 513 if (++n > sz) { | |
| 514 buf[0] = '\000'; | |
| 515 return NULL; | |
| 516 } | |
| 517 *start++ = '-'; | |
| 518 } | |
| 519 | |
| 520 // Loop until we have converted the entire number. Output at least one | |
| 521 // character (i.e. '0'). | |
| 522 char *ptr = start; | |
| 523 do { | |
| 524 // Make sure there is still enough space left in our output buffer. | |
| 525 if (++n > sz) { | |
| 526 buf[0] = '\000'; | |
| 527 return NULL; | |
| 528 } | |
| 529 | |
| 530 // Output the next digit. | |
| 531 *ptr++ = "0123456789abcdef"[j % base]; | |
| 532 j /= base; | |
| 533 | |
| 534 if (padding > 0) | |
| 535 padding--; | |
| 536 } while (j > 0 || padding > 0); | |
| 537 | |
| 538 // Terminate the output with a NUL character. | |
| 539 *ptr = '\000'; | |
| 540 | |
| 541 // Conversion to ASCII actually resulted in the digits being in reverse | |
| 542 // order. We can't easily generate them in forward order, as we can't tell | |
| 543 // the number of characters needed until we are done converting. | |
| 544 // So, now, we reverse the string (except for the possible "-" sign). | |
| 545 while (--ptr > start) { | |
| 546 char ch = *ptr; | |
| 547 *ptr = *start; | |
| 548 *start++ = ch; | |
| 549 } | |
| 550 return buf; | |
| 551 } | |
| 552 | |
| 553 } // namespace internal | |
| 554 | |
| 555 } // namespace debug | 484 } // namespace debug |
| 556 } // namespace base | 485 } // namespace base |
| OLD | NEW |