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
hb_options()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
_hb_debug(unsigned int level,unsigned int max_level)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
_hb_print_func(const char * func)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
_hb_debug_msg_va(const char * what,const void * obj,const char * func,bool indented,unsigned int level,int level_dir,const char * message,va_list ap)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)
_hb_debug_msg_va(const char * what HB_UNUSED,const void * obj HB_UNUSED,const char * func HB_UNUSED,bool indented HB_UNUSED,unsigned int level HB_UNUSED,int level_dir HB_UNUSED,const char * message HB_UNUSED,va_list ap HB_UNUSED)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)
_hb_debug_msg(const char * what,const void * obj,const char * func,bool indented,unsigned int level,int level_dir,const char * message,...)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)
_hb_debug_msg(const char * what HB_UNUSED,const void * obj HB_UNUSED,const char * func HB_UNUSED,bool indented HB_UNUSED,unsigned int level HB_UNUSED,int level_dir HB_UNUSED,const char * message HB_UNUSED,...)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 {
printhb_printer_t240 const char *print (const T&) { return "something"; }
241 };
242
243 template <>
244 struct hb_printer_t<bool> {
printhb_printer_t245 const char *print (bool v) { return v ? "true" : "false"; }
246 };
247
248 template <>
249 struct hb_printer_t<hb_void_t> {
printhb_printer_t250 const char *print (hb_void_t) { return ""; }
251 };
252
253
254 /*
255 * Trace
256 */
257
258 template <typename T>
_hb_warn_no_return(bool returned)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 <>
_hb_warn_no_return(bool returned HB_UNUSED)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 {
hb_auto_trace_thb_auto_trace_t272 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 }
~hb_auto_trace_thb_auto_trace_t287 ~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
rethb_auto_trace_t296 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 {
hb_auto_trace_thb_auto_trace_t323 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
rethb_auto_trace_t330 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 {
rethb_no_trace_t339 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