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