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