1 /*
2 * Copyright (c) 2016-2017 Dmitry V. Levin <ldv@strace.io>
3 * Copyright (c) 2017-2023 The strace developers.
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: LGPL-2.1-or-later
7 */
8
9 #ifndef STRACE_PRINT_FIELDS_H
10 # define STRACE_PRINT_FIELDS_H
11
12 # include "static_assert.h"
13
14 # ifdef IN_STRACE
15
16 # define STRACE_PRINTS(s_) tprints_string(s_)
17
18 /*
19 * The printf-like function to use in header files
20 * shared between strace and its tests.
21 */
22 # define STRACE_PRINTF tprintf_string
23
24 # else /* !IN_STRACE */
25
26 # include <stdio.h>
27
28 # define STRACE_PRINTS(s_) fputs((s_), stdout)
29
30 /*
31 * The printf-like function to use in header files
32 * shared between strace and its tests.
33 */
34 # define STRACE_PRINTF printf
35
36 # endif /* !IN_STRACE */
37
38
39 static inline void
40 tprint_struct_begin(void)
41 {
42 STRACE_PRINTS("{");
43 }
44
45 static inline void
46 tprint_struct_next(void)
47 {
48 STRACE_PRINTS(", ");
49 }
50
51 static inline void
52 tprint_struct_end(void)
53 {
54 STRACE_PRINTS("}");
55 }
56
57 static inline void
58 tprint_union_begin(void)
59 {
60 STRACE_PRINTS("{");
61 }
62
63 static inline void
64 tprint_union_next(void)
65 {
66 STRACE_PRINTS(", ");
67 }
68
69 static inline void
70 tprint_union_end(void)
71 {
72 STRACE_PRINTS("}");
73 }
74
75 static inline void
76 tprint_array_begin(void)
77 {
78 STRACE_PRINTS("[");
79 }
80
81 static inline void
82 tprint_array_next(void)
83 {
84 STRACE_PRINTS(", ");
85 }
86
87 static inline void
88 tprint_array_end(void)
89 {
90 STRACE_PRINTS("]");
91 }
92
93 static inline void
94 tprint_array_index_begin(void)
95 {
96 STRACE_PRINTS("[");
97 }
98
99 static inline void
100 tprint_array_index_equal(void)
101 {
102 STRACE_PRINTS("]=");
103 }
104
105 static inline void
106 tprint_array_index_end(void)
107 {
108 }
109
110 static inline void
111 tprint_arg_next(void)
112 {
113 STRACE_PRINTS(", ");
114 }
115
116 static inline void
117 tprint_arg_end(void)
118 {
119 STRACE_PRINTS(")");
120 }
121
122 static inline void
123 tprint_bitset_begin(void)
124 {
125 STRACE_PRINTS("[");
126 }
127
128 static inline void
129 tprint_bitset_next(void)
130 {
131 STRACE_PRINTS(" ");
132 }
133
134 static inline void
135 tprint_bitset_end(void)
136 {
137 STRACE_PRINTS("]");
138 }
139
140 static inline void
141 tprint_comment_begin(void)
142 {
143 STRACE_PRINTS(" /* ");
144 }
145
146 static inline void
147 tprint_comment_end(void)
148 {
149 STRACE_PRINTS(" */");
150 }
151
152 static inline void
153 tprint_indirect_begin(void)
154 {
155 STRACE_PRINTS("[");
156 }
157
158 static inline void
159 tprint_indirect_end(void)
160 {
161 STRACE_PRINTS("]");
162 }
163
164 static inline void
165 tprint_attribute_begin(void)
166 {
167 STRACE_PRINTS("[");
168 }
169
170 static inline void
171 tprint_attribute_end(void)
172 {
173 STRACE_PRINTS("]");
174 }
175
176 static inline void
177 tprint_associated_info_begin(void)
178 {
179 STRACE_PRINTS("<");
180 }
181
182 static inline void
183 tprint_associated_info_end(void)
184 {
185 STRACE_PRINTS(">");
186 }
187
188 static inline void
189 tprint_more_data_follows(void)
190 {
191 STRACE_PRINTS("...");
192 }
193
194 static inline void
195 tprint_value_changed(void)
196 {
197 STRACE_PRINTS(" => ");
198 }
199
200 static inline void
201 tprint_alternative_value(void)
202 {
203 STRACE_PRINTS(" or ");
204 }
205
206 static inline void
207 tprint_unavailable(void)
208 {
209 STRACE_PRINTS("???");
210 }
211
212 static inline void
213 tprint_shift_begin(void)
214 {
215 }
216
217 static inline void
218 tprint_shift_end(void)
219 {
220 }
221
222 static inline void
223 tprint_shift(void)
224 {
225 STRACE_PRINTS("<<");
226 }
227
228 static inline void
229 tprint_flags_begin(void)
230 {
231 }
232
233 static inline void
234 tprint_flags_or(void)
235 {
236 STRACE_PRINTS("|");
237 }
238
239 static inline void
240 tprint_flags_end(void)
241 {
242 }
243
244 static inline void
245 tprint_plus(void)
246 {
247 STRACE_PRINTS("+");
248 }
249
250 static inline void
251 tprint_space(void)
252 {
253 STRACE_PRINTS(" ");
254 }
255
256 static inline void
257 tprint_null(void)
258 {
259 STRACE_PRINTS("NULL");
260 }
261
262 static inline void
263 tprint_newline(void)
264 {
265 STRACE_PRINTS("\n");
266 }
267
268 static inline void
269 tprints_field_name(const char *name)
270 {
271 STRACE_PRINTF("%s=", name);
272 }
273
274 static inline void
275 tprints_arg_name_begin(const char *name)
276 {
277 STRACE_PRINTF("%s=", name);
278 }
279
280 static inline void
281 tprint_arg_name_end(void)
282 {
283 }
284
285 static inline void
286 tprints_arg_begin(const char *name)
287 {
288 STRACE_PRINTF("%s(", name);
289 }
290
291 static inline void
292 tprint_sysret_begin(void)
293 {
294 STRACE_PRINTS("=");
295 }
296
297 static inline void
298 tprints_sysret_next(const char *name)
299 {
300 tprint_space();
301 }
302
303 static inline void
304 tprints_sysret_string(const char *name, const char *str)
305 {
306 tprints_sysret_next(name);
307 STRACE_PRINTF("(%s)", str);
308 }
309
310 static inline void
311 tprint_sysret_pseudo_rval(void)
312 {
313 STRACE_PRINTS("?");
314 }
315
316 static inline void
317 tprint_sysret_end(void)
318 {
319 }
320
321 # define PRINT_VAL_D(val_) \
322 STRACE_PRINTF("%lld", sign_extend_unsigned_to_ll(val_))
323
324 # define PRINT_VAL_U(val_) \
325 STRACE_PRINTF("%llu", zero_extend_signed_to_ull(val_))
326
327 # define PRINT_VAL_X(val_) \
328 STRACE_PRINTF("%#llx", zero_extend_signed_to_ull(val_))
329
330 # define PRINT_VAL_03O(val_) \
331 STRACE_PRINTF("%#03llo", zero_extend_signed_to_ull(val_))
332
333 # define PRINT_VAL_0X(val_) \
334 STRACE_PRINTF("%#0*llx", (int) sizeof(val_) * 2, \
335 zero_extend_signed_to_ull(val_))
336
337 # define PRINT_VAL_ID(val_) \
338 do { \
339 if (sign_extend_unsigned_to_ll(val_) == -1LL) \
340 PRINT_VAL_D(-1); \
341 else \
342 PRINT_VAL_U(val_); \
343 } while (0)
344
345 # define PRINT_FIELD_D(where_, field_) \
346 do { \
347 tprints_field_name(#field_); \
348 PRINT_VAL_D((where_).field_); \
349 } while (0)
350
351 # define PRINT_FIELD_U(where_, field_) \
352 do { \
353 tprints_field_name(#field_); \
354 PRINT_VAL_U((where_).field_); \
355 } while (0)
356
357 # define PRINT_FIELD_U_CAST(where_, field_, type_) \
358 do { \
359 tprints_field_name(#field_); \
360 PRINT_VAL_U((type_)((where_).field_)); \
361 } while (0)
362
363 # define PRINT_FIELD_X(where_, field_) \
364 do { \
365 tprints_field_name(#field_); \
366 PRINT_VAL_X((where_).field_); \
367 } while (0)
368
369 # define PRINT_FIELD_X_CAST(where_, field_, type_) \
370 do { \
371 tprints_field_name(#field_); \
372 PRINT_VAL_X((type_)((where_).field_)); \
373 } while (0)
374
375 # define PRINT_FIELD_ADDR64(where_, field_) \
376 do { \
377 tprints_field_name(#field_); \
378 printaddr64((where_).field_); \
379 } while (0)
380
381 # define PRINT_FIELD_0X(where_, field_) \
382 do { \
383 tprints_field_name(#field_); \
384 PRINT_VAL_0X((where_).field_); \
385 } while (0)
386
387 # define PRINT_FIELD_VAL_ARRAY(where_, field_, print_val_) \
388 do { \
389 tprints_field_name(#field_); \
390 for (size_t i_ = 0; \
391 i_ < ARRAY_SIZE((where_).field_); \
392 ++i_) { \
393 if (i_) \
394 tprint_array_next(); \
395 else \
396 tprint_array_begin(); \
397 print_val_((where_).field_[i_]); \
398 } \
399 tprint_array_end(); \
400 } while (0)
401
402 # define PRINT_FIELD_D_ARRAY(where_, field_) \
403 PRINT_FIELD_VAL_ARRAY((where_), field_, PRINT_VAL_D)
404
405 # define PRINT_FIELD_U_ARRAY(where_, field_) \
406 PRINT_FIELD_VAL_ARRAY((where_), field_, PRINT_VAL_U)
407
408 # define PRINT_FIELD_X_ARRAY(where_, field_) \
409 PRINT_FIELD_VAL_ARRAY((where_), field_, PRINT_VAL_X)
410
411 # define PRINT_FIELD_VAL_ARRAY2D(where_, field_, print_val_) \
412 do { \
413 tprints_field_name(#field_); \
414 for (size_t i_ = 0; \
415 i_ < ARRAY_SIZE((where_).field_); \
416 ++i_) { \
417 if (i_) \
418 tprint_array_next(); \
419 else \
420 tprint_array_begin(); \
421 for (size_t j_ = 0; \
422 j_ < ARRAY_SIZE((where_).field_[i_]); \
423 ++j_) { \
424 if (j_) \
425 tprint_array_next(); \
426 else \
427 tprint_array_begin(); \
428 print_val_((where_).field_[i_][j_]); \
429 } \
430 tprint_array_end(); \
431 } \
432 tprint_array_end(); \
433 } while (0)
434
435 # define PRINT_FIELD_X_ARRAY2D(where_, field_) \
436 PRINT_FIELD_VAL_ARRAY2D((where_), field_, PRINT_VAL_X)
437
438 # define PRINT_FIELD_COOKIE(where_, field_) \
439 do { \
440 static_assert(ARRAY_SIZE((where_).field_) == 2, \
441 "unexpected array size"); \
442 PRINT_FIELD_U_ARRAY((where_), field_); \
443 } while (0)
444
445 # define PRINT_FIELD_FLAGS(where_, field_, xlat_, dflt_) \
446 do { \
447 tprints_field_name(#field_); \
448 printflags64((xlat_), \
449 zero_extend_signed_to_ull((where_).field_),\
450 (dflt_)); \
451 } while (0)
452
453 # define PRINT_FIELD_FLAGS_VERBOSE(where_, field_, xlat_, dflt_) \
454 do { \
455 tprints_field_name(#field_); \
456 tprint_flags_begin(); \
457 printflags_ex(zero_extend_signed_to_ull((where_).field_), \
458 (dflt_), \
459 xlat_verbose(xlat_verbosity) == XLAT_STYLE_RAW \
460 ? XLAT_STYLE_RAW : XLAT_STYLE_VERBOSE, \
461 (xlat_), NULL); \
462 tprint_flags_end(); \
463 } while (0)
464
465 # define PRINT_FIELD_XVAL(where_, field_, xlat_, dflt_) \
466 do { \
467 tprints_field_name(#field_); \
468 printxval64((xlat_), \
469 zero_extend_signed_to_ull((where_).field_), \
470 (dflt_)); \
471 } while (0)
472
473 # define PRINT_FIELD_XVAL_VERBOSE(where_, field_, xlat_, dflt_) \
474 do { \
475 tprints_field_name(#field_); \
476 printxval_ex((xlat_), \
477 zero_extend_signed_to_ull((where_).field_), \
478 (dflt_), \
479 xlat_verbose(xlat_verbosity) == XLAT_STYLE_RAW \
480 ? XLAT_STYLE_RAW : XLAT_STYLE_VERBOSE); \
481 } while (0)
482
483 # define PRINT_FIELD_XVAL_D(where_, field_, xlat_, dflt_) \
484 do { \
485 tprints_field_name(#field_); \
486 printxval64_d((xlat_), \
487 sign_extend_unsigned_to_ll((where_).field_), \
488 (dflt_)); \
489 } while (0)
490
491 # define PRINT_FIELD_XVAL_U(where_, field_, xlat_, dflt_) \
492 do { \
493 tprints_field_name(#field_); \
494 printxval64_u((xlat_), \
495 zero_extend_signed_to_ull((where_).field_), \
496 (dflt_)); \
497 } while (0)
498
499 # define PRINT_FIELD_XVAL_U_VERBOSE(where_, field_, xlat_, dflt_) \
500 do { \
501 tprints_field_name(#field_); \
502 printxval_ex((xlat_), \
503 zero_extend_signed_to_ull((where_).field_), \
504 (dflt_), \
505 (xlat_verbose(xlat_verbosity) == XLAT_STYLE_RAW \
506 ? XLAT_STYLE_RAW : XLAT_STYLE_VERBOSE) \
507 | XLAT_STYLE_FMT_U); \
508 } while (0)
509
510 # define PRINT_FIELD_ERR_D(where_, field_) \
511 do { \
512 tprints_field_name(#field_); \
513 print_err(sign_extend_unsigned_to_ll((where_).field_), \
514 true); \
515 } while (0)
516
517 # define PRINT_FIELD_ERR_U(where_, field_) \
518 do { \
519 tprints_field_name(#field_); \
520 print_err(zero_extend_signed_to_ull((where_).field_), \
521 false); \
522 } while (0)
523
524 /*
525 * Generic "ID" printing. ID is considered unsigned except for the special value
526 * of -1.
527 */
528 # define PRINT_FIELD_ID(where_, field_) \
529 do { \
530 tprints_field_name(#field_); \
531 PRINT_VAL_ID((where_).field_); \
532 } while (0)
533
534 # define PRINT_FIELD_UUID(where_, field_) \
535 do { \
536 tprints_field_name(#field_); \
537 print_uuid((const unsigned char *) ((where_).field_)); \
538 } while (0)
539
540 # define PRINT_FIELD_U64(where_, field_) \
541 do { \
542 tprints_field_name(#field_); \
543 if (zero_extend_signed_to_ull((where_).field_) == UINT64_MAX) \
544 print_xlat_u(UINT64_MAX); \
545 else \
546 PRINT_VAL_U((where_).field_); \
547 } while (0)
548
549 # define PRINT_FIELD_TICKS(where_, field_, freq_, precision_) \
550 do { \
551 tprints_field_name(#field_); \
552 print_ticks((where_).field_, (freq_), (precision_)); \
553 } while (0)
554
555 # define PRINT_FIELD_TICKS_D(where_, field_, freq_, precision_) \
556 do { \
557 tprints_field_name(#field_); \
558 print_ticks_d((where_).field_, (freq_), (precision_)); \
559 } while (0)
560
561 # define PRINT_FIELD_CLOCK_T(where_, field_) \
562 do { \
563 tprints_field_name(#field_); \
564 print_clock_t((where_).field_); \
565 } while (0)
566
567 # define PRINT_FIELD_STRING(where_, field_, len_, style_) \
568 do { \
569 tprints_field_name(#field_); \
570 print_quoted_string((const char *)(where_).field_, \
571 (len_), (style_)); \
572 } while (0)
573
574 # define PRINT_FIELD_CSTRING(where_, field_) \
575 do { \
576 tprints_field_name(#field_); \
577 print_quoted_cstring((const char *) (where_).field_, \
578 sizeof((where_).field_) + \
579 MUST_BE_ARRAY((where_).field_)); \
580 } while (0)
581
582 # define PRINT_FIELD_CSTRING_SZ(where_, field_, size_) \
583 do { \
584 tprints_field_name(#field_); \
585 print_quoted_cstring((const char *) (where_).field_, \
586 (size_)); \
587 } while (0)
588
589 # define PRINT_FIELD_ARRAY(where_, field_, tcp_, print_func_) \
590 do { \
591 tprints_field_name(#field_); \
592 print_local_array((tcp_), (where_).field_, \
593 (print_func_)); \
594 } while (0)
595
596 # define PRINT_FIELD_ARRAY_INDEXED(where_, field_, tcp_, print_func_, \
597 ind_xlat_, ind_dflt_) \
598 do { \
599 tprints_field_name(#field_); \
600 print_local_array_ex((tcp_), (where_).field_, \
601 ARRAY_SIZE((where_).field_), \
602 sizeof(((where_).field_)[0]), \
603 (print_func_), \
604 NULL, PAF_PRINT_INDICES | XLAT_STYLE_FMT_U, \
605 (ind_xlat_), (ind_dflt_)); \
606 } while (0)
607
608 # define PRINT_FIELD_ARRAY_UPTO(where_, field_, \
609 upto_, tcp_, print_func_) \
610 do { \
611 tprints_field_name(#field_); \
612 print_local_array_upto((tcp_), (where_).field_, \
613 (upto_), (print_func_)); \
614 } while (0)
615
616 # define PRINT_FIELD_HEX_ARRAY(where_, field_) \
617 do { \
618 tprints_field_name(#field_); \
619 print_quoted_string((const char *)(where_).field_, \
620 sizeof((where_).field_) + \
621 MUST_BE_ARRAY((where_).field_), \
622 QUOTE_FORCE_HEX); \
623 } while (0)
624
625 # define PRINT_FIELD_HEX_ARRAY_UPTO(where_, field_, upto_) \
626 do { \
627 tprints_field_name(#field_); \
628 print_quoted_string((const char *)(where_).field_, \
629 (upto_), QUOTE_FORCE_HEX); \
630 } while (0)
631
632 # define PRINT_FIELD_INET_ADDR(where_, field_, af_) \
633 print_inet_addr((af_), &(where_).field_, \
634 sizeof((where_).field_), #field_)
635
636 # define PRINT_FIELD_NET_PORT(where_, field_) \
637 do { \
638 tprints_field_name(#field_); \
639 \
640 if (xlat_verbose(xlat_verbosity) != XLAT_STYLE_ABBREV) \
641 print_quoted_string((const char *) \
642 &(where_).field_, \
643 sizeof((where_).field_), \
644 QUOTE_FORCE_HEX); \
645 \
646 if (xlat_verbose(xlat_verbosity) == XLAT_STYLE_RAW) \
647 break; \
648 \
649 if (xlat_verbose(xlat_verbosity) \
650 == XLAT_STYLE_VERBOSE) \
651 tprint_comment_begin(); \
652 \
653 tprints_arg_begin("htons"); \
654 unsigned short us_ = ntohs((where_).field_); \
655 PRINT_VAL_U(us_); \
656 tprint_arg_end(); \
657 \
658 if (xlat_verbose(xlat_verbosity) \
659 == XLAT_STYLE_VERBOSE) \
660 tprint_comment_end(); \
661 } while (0)
662
663 # define PRINT_FIELD_IFINDEX(where_, field_) \
664 do { \
665 tprints_field_name(#field_); \
666 print_ifindex((where_).field_); \
667 } while (0)
668
669 # define PRINT_FIELD_SOCKADDR(where_, field_, tcp_) \
670 do { \
671 tprints_field_name(#field_); \
672 print_sockaddr(tcp_, &(where_).field_, \
673 sizeof((where_).field_)); \
674 } while (0)
675
676 # define PRINT_FIELD_DEV(where_, field_) \
677 do { \
678 tprints_field_name(#field_); \
679 print_dev_t((where_).field_); \
680 } while (0)
681
682 # define PRINT_FIELD_PTR(where_, field_) \
683 do { \
684 tprints_field_name(#field_); \
685 printaddr((mpers_ptr_t) (where_).field_); \
686 } while (0)
687
688 # define PRINT_FIELD_FD(where_, field_, tcp_) \
689 do { \
690 tprints_field_name(#field_); \
691 printfd((tcp_), (where_).field_); \
692 } while (0)
693
694 # define PRINT_FIELD_DIRFD(where_, field_, tcp_) \
695 do { \
696 tprints_field_name(#field_); \
697 print_dirfd((tcp_), (where_).field_); \
698 } while (0)
699
700 # define PRINT_FIELD_CHAR(where_, field_, flags_) \
701 do { \
702 tprints_field_name(#field_); \
703 print_char((where_).field_, (flags_)); \
704 } while (0)
705
706 # define PRINT_FIELD_TGID(where_, field_, tcp_) \
707 do { \
708 tprints_field_name(#field_); \
709 printpid((tcp_), (where_).field_, PT_TGID); \
710 } while (0)
711
712 # define PRINT_FIELD_SYSCALL_NAME(where_, field_, audit_arch_) \
713 do { \
714 tprints_field_name(#field_); \
715 const char *nr_prefix_ = NULL; \
716 const char *name = syscall_name_arch((where_).field_, \
717 (audit_arch_), &nr_prefix_); \
718 if (xlat_verbose(xlat_verbosity) != XLAT_STYLE_ABBREV \
719 || !nr_prefix_) \
720 PRINT_VAL_U((where_).field_); \
721 if (xlat_verbose(xlat_verbosity) == XLAT_STYLE_RAW \
722 || !name) \
723 break; \
724 if (!nr_prefix_ || \
725 xlat_verbose(xlat_verbosity) == XLAT_STYLE_VERBOSE) \
726 tprint_comment_begin(); \
727 if (nr_prefix_) \
728 tprints_string(nr_prefix_); \
729 tprints_string(name); \
730 if (!nr_prefix_ || \
731 xlat_verbose(xlat_verbosity) == XLAT_STYLE_VERBOSE) \
732 tprint_comment_end(); \
733 } while (0)
734
735 # define PRINT_FIELD_MAC(where_, field_) \
736 PRINT_FIELD_MAC_SZ((where_), field_, ARRAY_SIZE((where_).field_))
737
738 # define PRINT_FIELD_MAC_SZ(where_, field_, size_) \
739 do { \
740 static_assert(sizeof(((where_).field_)[0]) == 1, \
741 "MAC address is not a byte array"); \
742 tprints_field_name(#field_); \
743 print_mac_addr("", (const uint8_t *) ((where_).field_), \
744 MIN((size_), ARRAY_SIZE((where_).field_))); \
745 } while (0)
746
747 # define PRINT_FIELD_HWADDR_SZ(where_, field_, size_, hwtype_) \
748 do { \
749 static_assert(sizeof(((where_).field_)[0]) == 1, \
750 "hwaddress is not a byte array"); \
751 tprints_field_name(#field_); \
752 print_hwaddr("", (const uint8_t *) ((where_).field_), \
753 (size_), (hwtype_)); \
754 } while (0)
755
756
757 # define PRINT_FIELD_OBJ_PTR(where_, field_, print_func_, ...) \
758 do { \
759 tprints_field_name(#field_); \
760 (print_func_)(&((where_).field_), ##__VA_ARGS__); \
761 } while (0)
762
763 # define PRINT_FIELD_OBJ_TCB_PTR(where_, field_, \
764 tcp_, print_func_, ...) \
765 do { \
766 tprints_field_name(#field_); \
767 (print_func_)((tcp_), &((where_).field_), \
768 ##__VA_ARGS__); \
769 } while (0)
770
771 # define PRINT_FIELD_OBJ_VAL(where_, field_, print_func_, ...) \
772 do { \
773 tprints_field_name(#field_); \
774 (print_func_)((where_).field_, ##__VA_ARGS__); \
775 } while (0)
776
777 # define PRINT_FIELD_OBJ_U(where_, field_, print_func_, ...) \
778 do { \
779 tprints_field_name(#field_); \
780 (print_func_)(zero_extend_signed_to_ull((where_).field_),\
781 ##__VA_ARGS__); \
782 } while (0)
783
784 # define PRINT_FIELD_OBJ_TCB_VAL(where_, field_, \
785 tcp_, print_func_, ...) \
786 do { \
787 tprints_field_name(#field_); \
788 (print_func_)((tcp_), (where_).field_, ##__VA_ARGS__); \
789 } while (0)
790
791
792 # define MAYBE_PRINT_FIELD_LEN(print_prefix_, where_, field_, \
793 len_, print_func_, ...) \
794 do { \
795 unsigned int start = offsetof(typeof(where_), field_); \
796 unsigned int end = start + sizeof((where_).field_); \
797 if (len_ > start) { \
798 print_prefix_; \
799 if (len_ >= end) { \
800 print_func_((where_), field_, \
801 ##__VA_ARGS__); \
802 } else { \
803 tprints_field_name(#field_); \
804 print_quoted_string( \
805 (void *)&(where_).field_, \
806 len_ - start, QUOTE_FORCE_HEX); \
807 } \
808 } \
809 } while (0)
810
811 #endif /* !STRACE_PRINT_FIELDS_H */