Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 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_FORMAT_H_ | |
| 6 #define BASE_DEBUG_FORMAT_H_ | |
| 7 | |
| 8 #include <stddef.h> | |
| 9 #include <stdint.h> | |
| 10 #include <stdlib.h> | |
| 11 | |
| 12 #if defined(__unix__) | |
| 13 // For ssize_t | |
| 14 #include <unistd.h> | |
| 15 #endif | |
| 16 | |
| 17 #include "base/base_export.h" | |
| 18 #include "base/basictypes.h" | |
| 19 | |
| 20 namespace base { | |
| 21 namespace debug { | |
| 22 | |
| 23 #if defined(_MSC_VER) | |
| 24 // Define ssize_t inside of our namespace. | |
| 25 typedef long ssize_t; | |
| 26 #endif | |
| 27 | |
| 28 // Format() is a type-safe and async-signal-safe version of snprintf(). | |
| 29 // | |
| 30 // FormatN() is an alternative function signature that can be used when | |
| 31 // not dealing with fixed-sized buffers. When possible, Format() should always | |
| 32 // be used in favor of FormatN() | |
| 33 // | |
| 34 // These functions allow for formatting complicated messages from contexts that | |
| 35 // require strict async-signal-safety. In fact, it is safe to call them from | |
| 36 // any low-level execution context; even from contexts that have stricter | |
| 37 // requirements than just async-signal-safety. | |
| 38 // | |
| 39 // The code currently only supports a subset of format characters: | |
| 40 // %c, %d, %x, %X, %p, and %s. | |
|
Jeffrey Yasskin
2013/07/30 23:13:53
Do you require %c to match characters or can it ta
| |
| 41 // | |
| 42 // All format characters take an optional width parameter. This must be a | |
| 43 // positive integer. For %d, %x, %X and %p, if the width starts with | |
| 44 // a leading '0', padding is done with '0' instead of ' ' characters. | |
| 45 // | |
| 46 // Returns the number of bytes needed to store the untruncated output. This | |
| 47 // does not include the terminating NUL byte. | |
| 48 // | |
| 49 // Returns -1, iff a fatal error happened. This typically can only happen, if | |
| 50 // the buffer size is a) negative, b) zero (i.e. not even the NUL byte can be | |
| 51 // written), or c) bigger than MAXINT. | |
| 52 // | |
| 53 // While the code supports type checking and while it is generally very careful | |
| 54 // to avoid printing incorrect values, it tends to be conservative in printing | |
| 55 // as much as possible, even when given incorrect parameters. Typically, in | |
| 56 // case of an error, the format string will not be expanded. (i.e. something | |
| 57 // like Format(buf, "%p %d", 1, 2) results in "%p 2"). | |
| 58 // | |
| 59 // The pre-C++11 version cannot handle more than ten arguments. | |
| 60 // | |
| 61 // Basic example: | |
| 62 // char buf[20]; | |
| 63 // base::debug::Format(buf, "The answer: %2d", 42); | |
| 64 // | |
| 65 // Example with dynamically sized buffer (async-signal-safe); this code won't | |
| 66 // work on Visual studio, as it requires dynamically allocating arrays on the | |
| 67 // stack: | |
| 68 // const size_t kInitialSize = 128; | |
| 69 // size_t sz = kInitialSize-1; | |
| 70 // for (;;) { | |
| 71 // char buf[sz+1]; | |
| 72 // sz = FormatN(buf, sz, "Error message \"%s\"\n", err); | |
|
Jeffrey Yasskin
2013/07/30 23:13:53
The argument should be sz+1, right? (This interfac
| |
| 73 // if (sz >= sizeof(buf)) | |
| 74 // continue; | |
| 75 // write(2, buf, sz); | |
| 76 // break; | |
| 77 // } | |
| 78 | |
| 79 namespace internal { | |
| 80 // Helpers that use C++ overloading, templates, and specializations to deduce | |
| 81 // and record type information from function arguments. This allows us to | |
| 82 // later write a type-safe version of snprintf(). | |
| 83 | |
| 84 struct Arg { | |
| 85 // Any integer-like value. | |
| 86 Arg(signed char c) : i_(c), width_(sizeof(char)), type_(INT) { } | |
| 87 Arg(unsigned char c) : i_(c), width_(sizeof(char)), type_(UINT) { } | |
| 88 Arg(signed short i) : i_(i), width_(sizeof(short)), type_(INT) { } | |
| 89 Arg(unsigned short i) : i_(i), width_(sizeof(short)), type_(UINT) { } | |
| 90 Arg(signed int i) : i_(i), width_(sizeof(int)), type_(INT) { } | |
| 91 Arg(unsigned int i) : i_(i), width_(sizeof(int)), type_(UINT) { } | |
| 92 Arg(signed long i) : i_(i), width_(sizeof(long)), type_(INT) { } | |
| 93 Arg(unsigned long i) : i_(i), width_(sizeof(long)), type_(UINT) { } | |
| 94 Arg(signed long long i) : i_(i), width_(sizeof(long long)), | |
| 95 type_(INT) { } | |
| 96 Arg(unsigned long long i) : i_(i), width_(sizeof(long long)), | |
| 97 type_(UINT) { } | |
| 98 | |
| 99 // A C-style text string. | |
| 100 Arg(const char* s) : s_(s), type_(STRING) { } | |
| 101 Arg(char* s) : s_(s), type_(STRING) { } | |
| 102 | |
| 103 // Any pointer value that can be cast to a "void*". | |
| 104 template<class T> Arg(T* ptr) : ptr_((void*)ptr), type_(POINTER) { } | |
| 105 | |
| 106 union { | |
| 107 // An integer-like value. | |
| 108 struct { | |
| 109 int64_t i_; | |
| 110 unsigned char width_; | |
| 111 }; | |
| 112 | |
| 113 // A C-style text string. | |
| 114 const char* s_; | |
| 115 | |
| 116 // A pointer to an arbitrary object. | |
| 117 const void* ptr_; | |
| 118 }; | |
| 119 const enum { INT, UINT, STRING, POINTER } type_; | |
| 120 }; | |
| 121 | |
| 122 // This is the internal function that performs the actual formatting of | |
| 123 // an snprintf()-style format string. | |
| 124 BASE_EXPORT ssize_t FormatN(char* buf, size_t sz, const char* fmt, | |
| 125 const Arg* args, size_t max_args); | |
| 126 } // namespace internal | |
| 127 | |
| 128 #if __cplusplus >= 201103 // C++11 | |
| 129 | |
| 130 template<typename... Args> | |
| 131 ssize_t FormatN(char* buf, size_t N, const char* fmt, Args... args) { | |
|
Jeffrey Yasskin
2013/07/30 23:13:53
You may want to attach the gcc format attribute (h
| |
| 132 // Use Arg() object to record type information and then copy arguments to an | |
| 133 // array to make it easier to iterate over them. | |
| 134 const internal::Arg arg_array[] = { args... }; | |
| 135 return internal::FormatN(buf, N, fmt, arg_array, | |
| 136 ARRAYSIZE_UNSAFE(arg_array)); | |
| 137 } | |
| 138 | |
| 139 template<size_t N, typename... Args> | |
| 140 ssize_t Format(char (&buf)[N], const char* fmt, Args... args) { | |
| 141 // Use Arg() object to record type information and then copy arguments to an | |
| 142 // array to make it easier to iterate over them. | |
| 143 const internal::Arg arg_array[] = { args... }; | |
| 144 return internal::FormatN(buf, N, fmt, arg_array, | |
| 145 ARRAYSIZE_UNSAFE(arg_array)); | |
| 146 } | |
| 147 | |
| 148 #else // Pre-C++11 | |
| 149 | |
| 150 // TODO(markus): C++11 has a much more concise and readable solution for | |
| 151 // expressing what we are doing here. Delete the fall-back code for older | |
| 152 // compilers as soon as we have fully switched to C++11 | |
| 153 | |
| 154 template<class T0, class T1, class T2, class T3, class T4, | |
| 155 class T5, class T6, class T7, class T8, class T9> | |
| 156 ssize_t FormatN(char* buf, size_t N, const char* fmt, | |
| 157 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, | |
| 158 T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { | |
| 159 // Use Arg() object to record type information and then copy arguments to an | |
| 160 // array to make it easier to iterate over them. | |
| 161 const internal::Arg arg_array[] = { | |
| 162 arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 | |
| 163 }; | |
| 164 return internal::FormatN(buf, N, fmt, arg_array, | |
| 165 ARRAYSIZE_UNSAFE(arg_array)); | |
| 166 } | |
| 167 | |
| 168 template<size_t N, | |
| 169 class T0, class T1, class T2, class T3, class T4, | |
| 170 class T5, class T6, class T7, class T8, class T9> | |
| 171 ssize_t Format(char (&buf)[N], const char* fmt, | |
| 172 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, | |
| 173 T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { | |
| 174 // Use Arg() object to record type information and then copy arguments to an | |
| 175 // array to make it easier to iterate over them. | |
| 176 const internal::Arg arg_array[] = { | |
| 177 arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 | |
| 178 }; | |
| 179 return internal::FormatN(buf, N, fmt, arg_array, | |
| 180 ARRAYSIZE_UNSAFE(arg_array)); | |
| 181 } | |
| 182 | |
| 183 template<class T0, class T1, class T2, class T3, class T4, | |
| 184 class T5, class T6, class T7, class T8> | |
| 185 ssize_t FormatN(char* buf, size_t N, const char* fmt, | |
| 186 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, | |
| 187 T5 arg5, T6 arg6, T7 arg7, T8 arg8) { | |
| 188 // Use Arg() object to record type information and then copy arguments to an | |
| 189 // array to make it easier to iterate over them. | |
| 190 const internal::Arg arg_array[] = { | |
| 191 arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 | |
| 192 }; | |
| 193 return internal::FormatN(buf, N, fmt, arg_array, | |
| 194 ARRAYSIZE_UNSAFE(arg_array)); | |
| 195 } | |
| 196 | |
| 197 template<size_t N, | |
| 198 class T0, class T1, class T2, class T3, class T4, class T5, | |
| 199 class T6, class T7, class T8> | |
| 200 ssize_t Format(char (&buf)[N], const char* fmt, | |
| 201 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, | |
| 202 T5 arg5, T6 arg6, T7 arg7, T8 arg8) { | |
| 203 // Use Arg() object to record type information and then copy arguments to an | |
| 204 // array to make it easier to iterate over them. | |
| 205 const internal::Arg arg_array[] = { | |
| 206 arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 | |
| 207 }; | |
| 208 return internal::FormatN(buf, N, fmt, arg_array, | |
| 209 ARRAYSIZE_UNSAFE(arg_array)); | |
|
Jeffrey Yasskin
2013/07/30 23:13:53
arraysize(arg_array) should work, since internal::
| |
| 210 } | |
| 211 | |
| 212 template<class T0, class T1, class T2, class T3, class T4, class T5, | |
| 213 class T6, class T7> | |
| 214 ssize_t FormatN(char* buf, size_t N, const char* fmt, | |
| 215 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, | |
| 216 T5 arg5, T6 arg6, T7 arg7) { | |
| 217 // Use Arg() object to record type information and then copy arguments to an | |
| 218 // array to make it easier to iterate over them. | |
| 219 const internal::Arg arg_array[] = { | |
| 220 arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 | |
| 221 }; | |
| 222 return internal::FormatN(buf, N, fmt, arg_array, | |
| 223 ARRAYSIZE_UNSAFE(arg_array)); | |
| 224 } | |
| 225 | |
| 226 template<size_t N, | |
| 227 class T0, class T1, class T2, class T3, class T4, class T5, | |
| 228 class T6, class T7> | |
| 229 ssize_t Format(char (&buf)[N], const char* fmt, | |
| 230 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, | |
| 231 T5 arg5, T6 arg6, T7 arg7) { | |
| 232 // Use Arg() object to record type information and then copy arguments to an | |
| 233 // array to make it easier to iterate over them. | |
| 234 const internal::Arg arg_array[] = { | |
| 235 arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 | |
| 236 }; | |
| 237 return internal::FormatN(buf, N, fmt, arg_array, | |
| 238 ARRAYSIZE_UNSAFE(arg_array)); | |
| 239 } | |
| 240 | |
| 241 template<class T0, class T1, class T2, class T3, class T4, class T5, | |
| 242 class T6> | |
| 243 ssize_t FormatN(char* buf, size_t N, const char* fmt, | |
| 244 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, | |
| 245 T5 arg5, T6 arg6) { | |
| 246 // Use Arg() object to record type information and then copy arguments to an | |
| 247 // array to make it easier to iterate over them. | |
| 248 const internal::Arg arg_array[] = { | |
| 249 arg0, arg1, arg2, arg3, arg4, arg5, arg6 | |
| 250 }; | |
| 251 return internal::FormatN(buf, N, fmt, arg_array, | |
| 252 ARRAYSIZE_UNSAFE(arg_array)); | |
| 253 } | |
| 254 | |
| 255 template<size_t N, | |
| 256 class T0, class T1, class T2, class T3, class T4, class T5, | |
| 257 class T6> | |
| 258 ssize_t Format(char (&buf)[N], const char* fmt, | |
| 259 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { | |
| 260 // Use Arg() object to record type information and then copy arguments to an | |
| 261 // array to make it easier to iterate over them. | |
| 262 const internal::Arg arg_array[] = { | |
| 263 arg0, arg1, arg2, arg3, arg4, arg5, arg6 | |
| 264 }; | |
| 265 return internal::FormatN(buf, N, fmt, arg_array, | |
| 266 ARRAYSIZE_UNSAFE(arg_array)); | |
| 267 } | |
| 268 | |
| 269 template<class T0, class T1, class T2, class T3, class T4, class T5> | |
| 270 ssize_t FormatN(char* buf, size_t N, const char* fmt, | |
| 271 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { | |
| 272 // Use Arg() object to record type information and then copy arguments to an | |
| 273 // array to make it easier to iterate over them. | |
| 274 const internal::Arg arg_array[] = { arg0, arg1, arg2, arg3, arg4, arg5 }; | |
| 275 return internal::FormatN(buf, N, fmt, arg_array, | |
| 276 ARRAYSIZE_UNSAFE(arg_array)); | |
| 277 } | |
| 278 | |
| 279 template<size_t N, | |
| 280 class T0, class T1, class T2, class T3, class T4, class T5> | |
| 281 ssize_t Format(char (&buf)[N], const char* fmt, | |
| 282 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { | |
| 283 // Use Arg() object to record type information and then copy arguments to an | |
| 284 // array to make it easier to iterate over them. | |
| 285 const internal::Arg arg_array[] = { arg0, arg1, arg2, arg3, arg4, arg5 }; | |
| 286 return internal::FormatN(buf, N, fmt, arg_array, | |
| 287 ARRAYSIZE_UNSAFE(arg_array)); | |
| 288 } | |
| 289 | |
| 290 template<class T0, class T1, class T2, class T3, class T4> | |
| 291 ssize_t FormatN(char* buf, size_t N, const char* fmt, | |
| 292 T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { | |
| 293 // Use Arg() object to record type information and then copy arguments to an | |
| 294 // array to make it easier to iterate over them. | |
| 295 const internal::Arg arg_array[] = { arg0, arg1, arg2, arg3, arg4 }; | |
| 296 return internal::FormatN(buf, N, fmt, arg_array, | |
| 297 ARRAYSIZE_UNSAFE(arg_array)); | |
| 298 } | |
| 299 | |
| 300 template<size_t N, class T0, class T1, class T2, class T3, class T4> | |
| 301 ssize_t Format(char (&buf)[N], const char* fmt, T0 arg0, T1 arg1, | |
| 302 T2 arg2, T3 arg3, T4 arg4) { | |
| 303 // Use Arg() object to record type information and then copy arguments to an | |
| 304 // array to make it easier to iterate over them. | |
| 305 const internal::Arg arg_array[] = { arg0, arg1, arg2, arg3, arg4 }; | |
| 306 return internal::FormatN(buf, N, fmt, arg_array, | |
| 307 ARRAYSIZE_UNSAFE(arg_array)); | |
| 308 } | |
| 309 | |
| 310 template<class T0, class T1, class T2, class T3> | |
| 311 ssize_t FormatN(char* buf, size_t N, const char* fmt, | |
| 312 T0 arg0, T1 arg1, T2 arg2, T3 arg3) { | |
| 313 // Use Arg() object to record type information and then copy arguments to an | |
| 314 // array to make it easier to iterate over them. | |
| 315 const internal::Arg arg_array[] = { arg0, arg1, arg2, arg3 }; | |
| 316 return internal::FormatN(buf, N, fmt, arg_array, | |
| 317 ARRAYSIZE_UNSAFE(arg_array)); | |
| 318 } | |
| 319 | |
| 320 template<size_t N, class T0, class T1, class T2, class T3> | |
| 321 ssize_t Format(char (&buf)[N], const char* fmt, | |
| 322 T0 arg0, T1 arg1, T2 arg2, T3 arg3) { | |
| 323 // Use Arg() object to record type information and then copy arguments to an | |
| 324 // array to make it easier to iterate over them. | |
| 325 const internal::Arg arg_array[] = { arg0, arg1, arg2, arg3 }; | |
| 326 return internal::FormatN(buf, N, fmt, arg_array, | |
| 327 ARRAYSIZE_UNSAFE(arg_array)); | |
| 328 } | |
| 329 | |
| 330 template<class T0, class T1, class T2> | |
| 331 ssize_t FormatN(char* buf, size_t N, const char* fmt, | |
| 332 T0 arg0, T1 arg1, T2 arg2) { | |
| 333 // Use Arg() object to record type information and then copy arguments to an | |
| 334 // array to make it easier to iterate over them. | |
| 335 const internal::Arg arg_array[] = { arg0, arg1, arg2 }; | |
| 336 return internal::FormatN(buf, N, fmt, arg_array, | |
| 337 ARRAYSIZE_UNSAFE(arg_array)); | |
| 338 } | |
| 339 | |
| 340 template<size_t N, class T0, class T1, class T2> | |
| 341 ssize_t Format(char (&buf)[N], const char* fmt, T0 arg0, T1 arg1, T2 arg2) { | |
| 342 // Use Arg() object to record type information and then copy arguments to an | |
| 343 // array to make it easier to iterate over them. | |
| 344 const internal::Arg arg_array[] = { arg0, arg1, arg2 }; | |
| 345 return internal::FormatN(buf, N, fmt, arg_array, | |
| 346 ARRAYSIZE_UNSAFE(arg_array)); | |
| 347 } | |
| 348 | |
| 349 template<class T0, class T1> | |
| 350 ssize_t FormatN(char* buf, size_t N, const char* fmt, T0 arg0, T1 arg1) { | |
| 351 // Use Arg() object to record type information and then copy arguments to an | |
| 352 // array to make it easier to iterate over them. | |
| 353 const internal::Arg arg_array[] = { arg0, arg1 }; | |
| 354 return internal::FormatN(buf, N, fmt, arg_array, | |
| 355 ARRAYSIZE_UNSAFE(arg_array)); | |
| 356 } | |
| 357 | |
| 358 template<size_t N, class T0, class T1> | |
| 359 ssize_t Format(char (&buf)[N], const char* fmt, T0 arg0, T1 arg1) { | |
| 360 // Use Arg() object to record type information and then copy arguments to an | |
| 361 // array to make it easier to iterate over them. | |
| 362 const internal::Arg arg_array[] = { arg0, arg1 }; | |
| 363 return internal::FormatN(buf, N, fmt, arg_array, | |
| 364 ARRAYSIZE_UNSAFE(arg_array)); | |
| 365 } | |
| 366 | |
| 367 template<class T0> | |
| 368 ssize_t FormatN(char* buf, size_t N, const char* fmt, T0 arg0) { | |
| 369 // Use Arg() object to record type information and then copy arguments to an | |
| 370 // array to make it easier to iterate over them. | |
| 371 const internal::Arg arg_array[] = { arg0 }; | |
| 372 return internal::FormatN(buf, N, fmt, arg_array, | |
| 373 ARRAYSIZE_UNSAFE(arg_array)); | |
| 374 } | |
| 375 | |
| 376 template<size_t N, class T0> | |
| 377 ssize_t Format(char (&buf)[N], const char* fmt, T0 arg0) { | |
| 378 // Use Arg() object to record type information and then copy arguments to an | |
| 379 // array to make it easier to iterate over them. | |
| 380 const internal::Arg arg_array[] = { arg0 }; | |
| 381 return internal::FormatN(buf, N, fmt, arg_array, | |
| 382 ARRAYSIZE_UNSAFE(arg_array)); | |
| 383 } | |
| 384 #endif | |
| 385 | |
| 386 // Fast-path when we don't actually need to substitute any arguments. | |
| 387 BASE_EXPORT ssize_t FormatN(char* buf, size_t N, const char* fmt); | |
| 388 template<size_t N> | |
| 389 inline ssize_t Format(char (&buf)[N], const char* fmt) { | |
| 390 return FormatN(buf, N, fmt); | |
| 391 } | |
| 392 | |
| 393 } // namespace debug | |
| 394 } // namespace base | |
| 395 | |
| 396 #endif // BASE_DEBUG_FORMAT_H_ | |
| OLD | NEW |