1 /* 2 * Copyright 2017 Google, Inc. 3 * 4 * This is part of HarfBuzz, a text shaping library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 * 24 * Google Author(s): Behdad Esfahbod 25 */ 26 27 #ifndef HB_DEBUG_HH 28 #define HB_DEBUG_HH 29 30 #include "hb.hh" 31 #include "hb-atomic.hh" 32 #include "hb-dsalgs.hh" 33 34 35 #ifndef HB_DEBUG 36 #define HB_DEBUG 0 37 #endif 38 39 40 /* 41 * Global runtime options. 42 */ 43 44 struct hb_options_t 45 { 46 bool unused : 1; /* In-case sign bit is here. */ 47 bool initialized : 1; 48 bool uniscribe_bug_compatible : 1; 49 bool aat : 1; 50 }; 51 52 union hb_options_union_t { 53 int i; 54 hb_options_t opts; 55 }; 56 static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), ""); 57 58 HB_INTERNAL void 59 _hb_options_init (); 60 61 extern HB_INTERNAL hb_atomic_int_t _hb_options; 62 63 static inline hb_options_t 64 hb_options () 65 { 66 /* Make a local copy, so we can access bitfield threadsafely. */ 67 hb_options_union_t u; 68 u.i = _hb_options.get_relaxed (); 69 70 if (unlikely (!u.i)) 71 { 72 _hb_options_init (); 73 u.i = _hb_options.get_relaxed (); 74 } 75 76 return u.opts; 77 } 78 79 80 /* 81 * Debug output (needs enabling at compile time.) 82 */ 83 84 static inline bool 85 _hb_debug (unsigned int level, 86 unsigned int max_level) 87 { 88 return level < max_level; 89 } 90 91 #define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT)) 92 #define DEBUG_ENABLED(WHAT) (DEBUG_LEVEL_ENABLED (WHAT, 0)) 93 94 static inline void 95 _hb_print_func (const char *func) 96 { 97 if (func) 98 { 99 unsigned int func_len = strlen (func); 100 /* Skip "static" */ 101 if (0 == strncmp (func, "static ", 7)) 102 func += 7; 103 /* Skip "typename" */ 104 if (0 == strncmp (func, "typename ", 9)) 105 func += 9; 106 /* Skip return type */ 107 const char *space = strchr (func, ' '); 108 if (space) 109 func = space + 1; 110 /* Skip parameter list */ 111 const char *paren = strchr (func, '('); 112 if (paren) 113 func_len = paren - func; 114 fprintf (stderr, "%.*s", func_len, func); 115 } 116 } 117 118 template <int max_level> static inline void 119 _hb_debug_msg_va (const char *what, 120 const void *obj, 121 const char *func, 122 bool indented, 123 unsigned int level, 124 int level_dir, 125 const char *message, 126 va_list ap) HB_PRINTF_FUNC(7, 0); 127 template <int max_level> static inline void 128 _hb_debug_msg_va (const char *what, 129 const void *obj, 130 const char *func, 131 bool indented, 132 unsigned int level, 133 int level_dir, 134 const char *message, 135 va_list ap) 136 { 137 if (!_hb_debug (level, max_level)) 138 return; 139 140 fprintf (stderr, "%-10s", what ? what : ""); 141 142 if (obj) 143 fprintf (stderr, "(%*p) ", (unsigned int) (2 * sizeof (void *)), obj); 144 else 145 fprintf (stderr, " %*s ", (unsigned int) (2 * sizeof (void *)), ""); 146 147 if (indented) { 148 #define VBAR "\342\224\202" /* U+2502 BOX DRAWINGS LIGHT VERTICAL */ 149 #define VRBAR "\342\224\234" /* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ 150 #define DLBAR "\342\225\256" /* U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT */ 151 #define ULBAR "\342\225\257" /* U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT */ 152 #define LBAR "\342\225\264" /* U+2574 BOX DRAWINGS LIGHT LEFT */ 153 static const char bars[] = 154 VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR 155 VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR 156 VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR 157 VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR 158 VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR; 159 fprintf (stderr, "%2u %s" VRBAR "%s", 160 level, 161 bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), 162 level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR); 163 } else 164 fprintf (stderr, " " VRBAR LBAR); 165 166 _hb_print_func (func); 167 168 if (message) 169 { 170 fprintf (stderr, ": "); 171 vfprintf (stderr, message, ap); 172 } 173 174 fprintf (stderr, "\n"); 175 } 176 template <> inline void HB_PRINTF_FUNC(7, 0) 177 _hb_debug_msg_va<0> (const char *what HB_UNUSED, 178 const void *obj HB_UNUSED, 179 const char *func HB_UNUSED, 180 bool indented HB_UNUSED, 181 unsigned int level HB_UNUSED, 182 int level_dir HB_UNUSED, 183 const char *message HB_UNUSED, 184 va_list ap HB_UNUSED) {} 185 186 template <int max_level> static inline void 187 _hb_debug_msg (const char *what, 188 const void *obj, 189 const char *func, 190 bool indented, 191 unsigned int level, 192 int level_dir, 193 const char *message, 194 ...) HB_PRINTF_FUNC(7, 8); 195 template <int max_level> static inline void HB_PRINTF_FUNC(7, 8) 196 _hb_debug_msg (const char *what, 197 const void *obj, 198 const char *func, 199 bool indented, 200 unsigned int level, 201 int level_dir, 202 const char *message, 203 ...) 204 { 205 va_list ap; 206 va_start (ap, message); 207 _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap); 208 va_end (ap); 209 } 210 template <> inline void 211 _hb_debug_msg<0> (const char *what HB_UNUSED, 212 const void *obj HB_UNUSED, 213 const char *func HB_UNUSED, 214 bool indented HB_UNUSED, 215 unsigned int level HB_UNUSED, 216 int level_dir HB_UNUSED, 217 const char *message HB_UNUSED, 218 ...) HB_PRINTF_FUNC(7, 8); 219 template <> inline void HB_PRINTF_FUNC(7, 8) 220 _hb_debug_msg<0> (const char *what HB_UNUSED, 221 const void *obj HB_UNUSED, 222 const char *func HB_UNUSED, 223 bool indented HB_UNUSED, 224 unsigned int level HB_UNUSED, 225 int level_dir HB_UNUSED, 226 const char *message HB_UNUSED, 227 ...) {} 228 229 #define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), nullptr, true, (LEVEL), (LEVEL_DIR), __VA_ARGS__) 230 #define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), nullptr, false, 0, 0, __VA_ARGS__) 231 #define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__) 232 233 234 /* 235 * Printer 236 */ 237 238 template <typename T> 239 struct hb_printer_t { 240 const char *print (const T&) { return "something"; } 241 }; 242 243 template <> 244 struct hb_printer_t<bool> { 245 const char *print (bool v) { return v ? "true" : "false"; } 246 }; 247 248 template <> 249 struct hb_printer_t<hb_void_t> { 250 const char *print (hb_void_t) { return ""; } 251 }; 252 253 254 /* 255 * Trace 256 */ 257 258 template <typename T> 259 static inline void _hb_warn_no_return (bool returned) 260 { 261 if (unlikely (!returned)) { 262 fprintf (stderr, "OUCH, returned with no call to return_trace(). This is a bug, please report.\n"); 263 } 264 } 265 template <> 266 /*static*/ inline void _hb_warn_no_return<hb_void_t> (bool returned HB_UNUSED) 267 {} 268 269 template <int max_level, typename ret_t> 270 struct hb_auto_trace_t 271 { 272 explicit inline hb_auto_trace_t (unsigned int *plevel_, 273 const char *what_, 274 const void *obj_, 275 const char *func, 276 const char *message, 277 ...) HB_PRINTF_FUNC(6, 7) 278 : plevel (plevel_), what (what_), obj (obj_), returned (false) 279 { 280 if (plevel) ++*plevel; 281 282 va_list ap; 283 va_start (ap, message); 284 _hb_debug_msg_va<max_level> (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap); 285 va_end (ap); 286 } 287 ~hb_auto_trace_t () 288 { 289 _hb_warn_no_return<ret_t> (returned); 290 if (!returned) { 291 _hb_debug_msg<max_level> (what, obj, nullptr, true, plevel ? *plevel : 1, -1, " "); 292 } 293 if (plevel) --*plevel; 294 } 295 296 ret_t ret (ret_t v, 297 const char *func = "", 298 unsigned int line = 0) 299 { 300 if (unlikely (returned)) { 301 fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n"); 302 return v; 303 } 304 305 _hb_debug_msg<max_level> (what, obj, func, true, plevel ? *plevel : 1, -1, 306 "return %s (line %d)", 307 hb_printer_t<ret_t>().print (v), line); 308 if (plevel) --*plevel; 309 plevel = nullptr; 310 returned = true; 311 return v; 312 } 313 314 private: 315 unsigned int *plevel; 316 const char *what; 317 const void *obj; 318 bool returned; 319 }; 320 template <typename ret_t> /* Make sure we don't use hb_auto_trace_t when not tracing. */ 321 struct hb_auto_trace_t<0, ret_t> 322 { 323 explicit inline hb_auto_trace_t (unsigned int *plevel_, 324 const char *what_, 325 const void *obj_, 326 const char *func, 327 const char *message, 328 ...) HB_PRINTF_FUNC(6, 7) {} 329 330 ret_t ret (ret_t v, 331 const char *func HB_UNUSED = nullptr, 332 unsigned int line HB_UNUSED = 0) { return v; } 333 }; 334 335 /* For disabled tracing; optimize out everything. 336 * https://github.com/harfbuzz/harfbuzz/pull/605 */ 337 template <typename ret_t> 338 struct hb_no_trace_t { 339 ret_t ret (ret_t v, 340 const char *func HB_UNUSED = "", 341 unsigned int line HB_UNUSED = 0) { return v; } 342 }; 343 344 #define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__) 345 346 347 /* 348 * Instances. 349 */ 350 351 #ifndef HB_DEBUG_ARABIC 352 #define HB_DEBUG_ARABIC (HB_DEBUG+0) 353 #endif 354 355 #ifndef HB_DEBUG_BLOB 356 #define HB_DEBUG_BLOB (HB_DEBUG+0) 357 #endif 358 359 #ifndef HB_DEBUG_CORETEXT 360 #define HB_DEBUG_CORETEXT (HB_DEBUG+0) 361 #endif 362 363 #ifndef HB_DEBUG_DIRECTWRITE 364 #define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0) 365 #endif 366 367 #ifndef HB_DEBUG_FT 368 #define HB_DEBUG_FT (HB_DEBUG+0) 369 #endif 370 371 #ifndef HB_DEBUG_GET_COVERAGE 372 #define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0) 373 #endif 374 375 #ifndef HB_DEBUG_OBJECT 376 #define HB_DEBUG_OBJECT (HB_DEBUG+0) 377 #endif 378 379 #ifndef HB_DEBUG_SHAPE_PLAN 380 #define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0) 381 #endif 382 383 #ifndef HB_DEBUG_UNISCRIBE 384 #define HB_DEBUG_UNISCRIBE (HB_DEBUG+0) 385 #endif 386 387 /* 388 * With tracing. 389 */ 390 391 #ifndef HB_DEBUG_APPLY 392 #define HB_DEBUG_APPLY (HB_DEBUG+0) 393 #endif 394 #if HB_DEBUG_APPLY 395 #define TRACE_APPLY(this) \ 396 hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \ 397 (&c->debug_depth, c->get_name (), this, HB_FUNC, \ 398 "idx %d gid %u lookup %d", \ 399 c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index) 400 #else 401 #define TRACE_APPLY(this) hb_no_trace_t<bool> trace 402 #endif 403 404 #ifndef HB_DEBUG_CLOSURE 405 #define HB_DEBUG_CLOSURE (HB_DEBUG+0) 406 #endif 407 #if HB_DEBUG_CLOSURE 408 #define TRACE_CLOSURE(this) \ 409 hb_auto_trace_t<HB_DEBUG_CLOSURE, hb_void_t> trace \ 410 (&c->debug_depth, c->get_name (), this, HB_FUNC, \ 411 " ") 412 #else 413 #define TRACE_CLOSURE(this) hb_no_trace_t<hb_void_t> trace HB_UNUSED 414 #endif 415 416 #ifndef HB_DEBUG_COLLECT_GLYPHS 417 #define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0) 418 #endif 419 #if HB_DEBUG_COLLECT_GLYPHS 420 #define TRACE_COLLECT_GLYPHS(this) \ 421 hb_auto_trace_t<HB_DEBUG_COLLECT_GLYPHS, hb_void_t> trace \ 422 (&c->debug_depth, c->get_name (), this, HB_FUNC, \ 423 " ") 424 #else 425 #define TRACE_COLLECT_GLYPHS(this) hb_no_trace_t<hb_void_t> trace HB_UNUSED 426 #endif 427 428 #ifndef HB_DEBUG_SANITIZE 429 #define HB_DEBUG_SANITIZE (HB_DEBUG+0) 430 #endif 431 #if HB_DEBUG_SANITIZE 432 #define TRACE_SANITIZE(this) \ 433 hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace \ 434 (&c->debug_depth, c->get_name (), this, HB_FUNC, \ 435 " "); 436 #else 437 #define TRACE_SANITIZE(this) hb_no_trace_t<bool> trace 438 #endif 439 440 #ifndef HB_DEBUG_SERIALIZE 441 #define HB_DEBUG_SERIALIZE (HB_DEBUG+0) 442 #endif 443 #if HB_DEBUG_SERIALIZE 444 #define TRACE_SERIALIZE(this) \ 445 hb_auto_trace_t<HB_DEBUG_SERIALIZE, bool> trace \ 446 (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ 447 " "); 448 #else 449 #define TRACE_SERIALIZE(this) hb_no_trace_t<bool> trace 450 #endif 451 452 #ifndef HB_DEBUG_SUBSET 453 #define HB_DEBUG_SUBSET (HB_DEBUG+0) 454 #endif 455 #if HB_DEBUG_SUBSET 456 #define TRACE_SUBSET(this) \ 457 hb_auto_trace_t<HB_DEBUG_SUBSET, bool> trace \ 458 (&c->debug_depth, c->get_name (), this, HB_FUNC, \ 459 " "); 460 #else 461 #define TRACE_SUBSET(this) hb_no_trace_t<bool> trace 462 #endif 463 464 #ifndef HB_DEBUG_WOULD_APPLY 465 #define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0) 466 #endif 467 #if HB_DEBUG_WOULD_APPLY 468 #define TRACE_WOULD_APPLY(this) \ 469 hb_auto_trace_t<HB_DEBUG_WOULD_APPLY, bool> trace \ 470 (&c->debug_depth, c->get_name (), this, HB_FUNC, \ 471 "%d glyphs", c->len); 472 #else 473 #define TRACE_WOULD_APPLY(this) hb_no_trace_t<bool> trace 474 #endif 475 476 #ifndef HB_DEBUG_DISPATCH 477 #define HB_DEBUG_DISPATCH ( \ 478 HB_DEBUG_APPLY + \ 479 HB_DEBUG_CLOSURE + \ 480 HB_DEBUG_COLLECT_GLYPHS + \ 481 HB_DEBUG_SANITIZE + \ 482 HB_DEBUG_SERIALIZE + \ 483 HB_DEBUG_SUBSET + \ 484 HB_DEBUG_WOULD_APPLY + \ 485 0) 486 #endif 487 #if HB_DEBUG_DISPATCH 488 #define TRACE_DISPATCH(this, format) \ 489 hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \ 490 (&c->debug_depth, c->get_name (), this, HB_FUNC, \ 491 "format %d", (int) format); 492 #else 493 #define TRACE_DISPATCH(this, format) hb_no_trace_t<typename context_t::return_t> trace 494 #endif 495 496 497 #endif /* HB_DEBUG_HH */ 498