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 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
52union hb_options_union_t {
53 int i;
54 hb_options_t opts;
55};
56static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), "");
57
58HB_INTERNAL void
59_hb_options_init ();
60
61extern HB_INTERNAL hb_atomic_int_t _hb_options;
62
63static inline hb_options_t
64hb_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
84static 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
94static 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
118template <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);
127template <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}
176template <> 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
186template <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);
195template <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}
210template <> 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);
219template <> 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
238template <typename T>
239struct hb_printer_t {
240 const char *print (const T&) { return "something"; }
241};
242
243template <>
244struct hb_printer_t<bool> {
245 const char *print (bool v) { return v ? "true" : "false"; }
246};
247
248template <>
249struct hb_printer_t<hb_void_t> {
250 const char *print (hb_void_t) { return ""; }
251};
252
253
254/*
255 * Trace
256 */
257
258template <typename T>
259static 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}
265template <>
266/*static*/ inline void _hb_warn_no_return<hb_void_t> (bool returned HB_UNUSED)
267{}
268
269template <int max_level, typename ret_t>
270struct 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};
320template <typename ret_t> /* Make sure we don't use hb_auto_trace_t when not tracing. */
321struct 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 */
337template <typename ret_t>
338struct 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_SANITIZE
405#define HB_DEBUG_SANITIZE (HB_DEBUG+0)
406#endif
407#if HB_DEBUG_SANITIZE
408#define TRACE_SANITIZE(this) \
409 hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace \
410 (&c->debug_depth, c->get_name (), this, HB_FUNC, \
411 " ");
412#else
413#define TRACE_SANITIZE(this) hb_no_trace_t<bool> trace
414#endif
415
416#ifndef HB_DEBUG_SERIALIZE
417#define HB_DEBUG_SERIALIZE (HB_DEBUG+0)
418#endif
419#if HB_DEBUG_SERIALIZE
420#define TRACE_SERIALIZE(this) \
421 hb_auto_trace_t<HB_DEBUG_SERIALIZE, bool> trace \
422 (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \
423 " ");
424#else
425#define TRACE_SERIALIZE(this) hb_no_trace_t<bool> trace
426#endif
427
428#ifndef HB_DEBUG_SUBSET
429#define HB_DEBUG_SUBSET (HB_DEBUG+0)
430#endif
431#if HB_DEBUG_SUBSET
432#define TRACE_SUBSET(this) \
433 hb_auto_trace_t<HB_DEBUG_SUBSET, bool> trace \
434 (&c->debug_depth, c->get_name (), this, HB_FUNC, \
435 " ");
436#else
437#define TRACE_SUBSET(this) hb_no_trace_t<bool> trace
438#endif
439
440#ifndef HB_DEBUG_WOULD_APPLY
441#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
442#endif
443#if HB_DEBUG_WOULD_APPLY
444#define TRACE_WOULD_APPLY(this) \
445 hb_auto_trace_t<HB_DEBUG_WOULD_APPLY, bool> trace \
446 (&c->debug_depth, c->get_name (), this, HB_FUNC, \
447 "%d glyphs", c->len);
448#else
449#define TRACE_WOULD_APPLY(this) hb_no_trace_t<bool> trace
450#endif
451
452#ifndef HB_DEBUG_DISPATCH
453#define HB_DEBUG_DISPATCH ( \
454 HB_DEBUG_APPLY + \
455 HB_DEBUG_SANITIZE + \
456 HB_DEBUG_SERIALIZE + \
457 HB_DEBUG_SUBSET + \
458 HB_DEBUG_WOULD_APPLY + \
459 0)
460#endif
461#if HB_DEBUG_DISPATCH
462#define TRACE_DISPATCH(this, format) \
463 hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
464 (&c->debug_depth, c->get_name (), this, HB_FUNC, \
465 "format %d", (int) format);
466#else
467#define TRACE_DISPATCH(this, format) hb_no_trace_t<typename context_t::return_t> trace
468#endif
469
470
471#endif /* HB_DEBUG_HH */
472