diff options
author | Jan Beulich <jbeulich@novell.com> | 2006-12-06 20:14:13 -0500 |
---|---|---|
committer | Andi Kleen <andi@basil.nowhere.org> | 2006-12-06 20:14:13 -0500 |
commit | 6d0185ea611276fdf81991d7774d396bdc1ae392 (patch) | |
tree | cfbea72e9df3816a63ba6debd3e591d89b2c63e1 /kernel/unwind.c | |
parent | 3807fd46e94ab9f09e5ee3bff5e6515a94e9b3c7 (diff) |
[PATCH] unwinder: Add debugging output to the Dwarf2 unwinder
Add debugging printks to the unwinder to allow easier debugging
when something goes wrong with it.
This can be controlled with the new unwinder_debug=N option
Most output is given by N=1
AK: Added documentation of unwinder_debug=
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Diffstat (limited to 'kernel/unwind.c')
-rw-r--r-- | kernel/unwind.c | 113 |
1 files changed, 96 insertions, 17 deletions
diff --git a/kernel/unwind.c b/kernel/unwind.c index 7e721f104105..209e248517db 100644 --- a/kernel/unwind.c +++ b/kernel/unwind.c | |||
@@ -137,6 +137,17 @@ struct unwind_state { | |||
137 | 137 | ||
138 | static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; | 138 | static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; |
139 | 139 | ||
140 | static unsigned unwind_debug; | ||
141 | static int __init unwind_debug_setup(char *s) | ||
142 | { | ||
143 | unwind_debug = simple_strtoul(s, NULL, 0); | ||
144 | return 1; | ||
145 | } | ||
146 | __setup("unwind_debug=", unwind_debug_setup); | ||
147 | #define dprintk(lvl, fmt, args...) \ | ||
148 | ((void)(lvl > unwind_debug \ | ||
149 | || printk(KERN_DEBUG "unwind: " fmt "\n", ##args))) | ||
150 | |||
140 | static struct unwind_table *find_table(unsigned long pc) | 151 | static struct unwind_table *find_table(unsigned long pc) |
141 | { | 152 | { |
142 | struct unwind_table *table; | 153 | struct unwind_table *table; |
@@ -281,6 +292,7 @@ static void __init setup_unwind_table(struct unwind_table *table, | |||
281 | 292 | ||
282 | hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int) | 293 | hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int) |
283 | + 2 * n * sizeof(unsigned long); | 294 | + 2 * n * sizeof(unsigned long); |
295 | dprintk(2, "Binary lookup table size for %s: %lu bytes", table->name, hdrSize); | ||
284 | header = alloc(hdrSize); | 296 | header = alloc(hdrSize); |
285 | if (!header) | 297 | if (!header) |
286 | return; | 298 | return; |
@@ -500,13 +512,17 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
500 | const unsigned long *pul; | 512 | const unsigned long *pul; |
501 | } ptr; | 513 | } ptr; |
502 | 514 | ||
503 | if (ptrType < 0 || ptrType == DW_EH_PE_omit) | 515 | if (ptrType < 0 || ptrType == DW_EH_PE_omit) { |
516 | dprintk(1, "Invalid pointer encoding %02X (%p,%p).", ptrType, *pLoc, end); | ||
504 | return 0; | 517 | return 0; |
518 | } | ||
505 | ptr.p8 = *pLoc; | 519 | ptr.p8 = *pLoc; |
506 | switch(ptrType & DW_EH_PE_FORM) { | 520 | switch(ptrType & DW_EH_PE_FORM) { |
507 | case DW_EH_PE_data2: | 521 | case DW_EH_PE_data2: |
508 | if (end < (const void *)(ptr.p16u + 1)) | 522 | if (end < (const void *)(ptr.p16u + 1)) { |
523 | dprintk(1, "Data16 overrun (%p,%p).", ptr.p8, end); | ||
509 | return 0; | 524 | return 0; |
525 | } | ||
510 | if(ptrType & DW_EH_PE_signed) | 526 | if(ptrType & DW_EH_PE_signed) |
511 | value = get_unaligned(ptr.p16s++); | 527 | value = get_unaligned(ptr.p16s++); |
512 | else | 528 | else |
@@ -514,8 +530,10 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
514 | break; | 530 | break; |
515 | case DW_EH_PE_data4: | 531 | case DW_EH_PE_data4: |
516 | #ifdef CONFIG_64BIT | 532 | #ifdef CONFIG_64BIT |
517 | if (end < (const void *)(ptr.p32u + 1)) | 533 | if (end < (const void *)(ptr.p32u + 1)) { |
534 | dprintk(1, "Data32 overrun (%p,%p).", ptr.p8, end); | ||
518 | return 0; | 535 | return 0; |
536 | } | ||
519 | if(ptrType & DW_EH_PE_signed) | 537 | if(ptrType & DW_EH_PE_signed) |
520 | value = get_unaligned(ptr.p32s++); | 538 | value = get_unaligned(ptr.p32s++); |
521 | else | 539 | else |
@@ -527,8 +545,10 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
527 | BUILD_BUG_ON(sizeof(u32) != sizeof(value)); | 545 | BUILD_BUG_ON(sizeof(u32) != sizeof(value)); |
528 | #endif | 546 | #endif |
529 | case DW_EH_PE_native: | 547 | case DW_EH_PE_native: |
530 | if (end < (const void *)(ptr.pul + 1)) | 548 | if (end < (const void *)(ptr.pul + 1)) { |
549 | dprintk(1, "DataUL overrun (%p,%p).", ptr.p8, end); | ||
531 | return 0; | 550 | return 0; |
551 | } | ||
532 | value = get_unaligned(ptr.pul++); | 552 | value = get_unaligned(ptr.pul++); |
533 | break; | 553 | break; |
534 | case DW_EH_PE_leb128: | 554 | case DW_EH_PE_leb128: |
@@ -536,10 +556,14 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
536 | value = ptrType & DW_EH_PE_signed | 556 | value = ptrType & DW_EH_PE_signed |
537 | ? get_sleb128(&ptr.p8, end) | 557 | ? get_sleb128(&ptr.p8, end) |
538 | : get_uleb128(&ptr.p8, end); | 558 | : get_uleb128(&ptr.p8, end); |
539 | if ((const void *)ptr.p8 > end) | 559 | if ((const void *)ptr.p8 > end) { |
560 | dprintk(1, "DataLEB overrun (%p,%p).", ptr.p8, end); | ||
540 | return 0; | 561 | return 0; |
562 | } | ||
541 | break; | 563 | break; |
542 | default: | 564 | default: |
565 | dprintk(2, "Cannot decode pointer type %02X (%p,%p).", | ||
566 | ptrType, ptr.p8, end); | ||
543 | return 0; | 567 | return 0; |
544 | } | 568 | } |
545 | switch(ptrType & DW_EH_PE_ADJUST) { | 569 | switch(ptrType & DW_EH_PE_ADJUST) { |
@@ -549,11 +573,16 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
549 | value += (unsigned long)*pLoc; | 573 | value += (unsigned long)*pLoc; |
550 | break; | 574 | break; |
551 | default: | 575 | default: |
576 | dprintk(2, "Cannot adjust pointer type %02X (%p,%p).", | ||
577 | ptrType, *pLoc, end); | ||
552 | return 0; | 578 | return 0; |
553 | } | 579 | } |
554 | if ((ptrType & DW_EH_PE_indirect) | 580 | if ((ptrType & DW_EH_PE_indirect) |
555 | && probe_kernel_address((unsigned long *)value, value)) | 581 | && probe_kernel_address((unsigned long *)value, value)) { |
582 | dprintk(1, "Cannot read indirect value %lx (%p,%p).", | ||
583 | value, *pLoc, end); | ||
556 | return 0; | 584 | return 0; |
585 | } | ||
557 | *pLoc = ptr.p8; | 586 | *pLoc = ptr.p8; |
558 | 587 | ||
559 | return value; | 588 | return value; |
@@ -702,8 +731,10 @@ static int processCFI(const u8 *start, | |||
702 | state->label = NULL; | 731 | state->label = NULL; |
703 | return 1; | 732 | return 1; |
704 | } | 733 | } |
705 | if (state->stackDepth >= MAX_STACK_DEPTH) | 734 | if (state->stackDepth >= MAX_STACK_DEPTH) { |
735 | dprintk(1, "State stack overflow (%p,%p).", ptr.p8, end); | ||
706 | return 0; | 736 | return 0; |
737 | } | ||
707 | state->stack[state->stackDepth++] = ptr.p8; | 738 | state->stack[state->stackDepth++] = ptr.p8; |
708 | break; | 739 | break; |
709 | case DW_CFA_restore_state: | 740 | case DW_CFA_restore_state: |
@@ -718,8 +749,10 @@ static int processCFI(const u8 *start, | |||
718 | result = processCFI(start, end, 0, ptrType, state); | 749 | result = processCFI(start, end, 0, ptrType, state); |
719 | state->loc = loc; | 750 | state->loc = loc; |
720 | state->label = label; | 751 | state->label = label; |
721 | } else | 752 | } else { |
753 | dprintk(1, "State stack underflow (%p,%p).", ptr.p8, end); | ||
722 | return 0; | 754 | return 0; |
755 | } | ||
723 | break; | 756 | break; |
724 | case DW_CFA_def_cfa: | 757 | case DW_CFA_def_cfa: |
725 | state->cfa.reg = get_uleb128(&ptr.p8, end); | 758 | state->cfa.reg = get_uleb128(&ptr.p8, end); |
@@ -751,6 +784,7 @@ static int processCFI(const u8 *start, | |||
751 | break; | 784 | break; |
752 | case DW_CFA_GNU_window_save: | 785 | case DW_CFA_GNU_window_save: |
753 | default: | 786 | default: |
787 | dprintk(1, "Unrecognized CFI op %02X (%p,%p).", ptr.p8[-1], ptr.p8 - 1, end); | ||
754 | result = 0; | 788 | result = 0; |
755 | break; | 789 | break; |
756 | } | 790 | } |
@@ -766,12 +800,17 @@ static int processCFI(const u8 *start, | |||
766 | set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); | 800 | set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); |
767 | break; | 801 | break; |
768 | } | 802 | } |
769 | if (ptr.p8 > end) | 803 | if (ptr.p8 > end) { |
804 | dprintk(1, "Data overrun (%p,%p).", ptr.p8, end); | ||
770 | result = 0; | 805 | result = 0; |
806 | } | ||
771 | if (result && targetLoc != 0 && targetLoc < state->loc) | 807 | if (result && targetLoc != 0 && targetLoc < state->loc) |
772 | return 1; | 808 | return 1; |
773 | } | 809 | } |
774 | 810 | ||
811 | if (result && ptr.p8 < end) | ||
812 | dprintk(1, "Data underrun (%p,%p).", ptr.p8, end); | ||
813 | |||
775 | return result | 814 | return result |
776 | && ptr.p8 == end | 815 | && ptr.p8 == end |
777 | && (targetLoc == 0 | 816 | && (targetLoc == 0 |
@@ -843,6 +882,8 @@ int unwind(struct unwind_frame_info *frame) | |||
843 | hdr[3]); | 882 | hdr[3]); |
844 | } | 883 | } |
845 | } | 884 | } |
885 | if(hdr && !fde) | ||
886 | dprintk(3, "Binary lookup for %lx failed.", pc); | ||
846 | 887 | ||
847 | if (fde != NULL) { | 888 | if (fde != NULL) { |
848 | cie = cie_for_fde(fde, table); | 889 | cie = cie_for_fde(fde, table); |
@@ -864,6 +905,8 @@ int unwind(struct unwind_frame_info *frame) | |||
864 | fde = NULL; | 905 | fde = NULL; |
865 | } else | 906 | } else |
866 | fde = NULL; | 907 | fde = NULL; |
908 | if(!fde) | ||
909 | dprintk(1, "Binary lookup result for %lx discarded.", pc); | ||
867 | } | 910 | } |
868 | if (fde == NULL) { | 911 | if (fde == NULL) { |
869 | for (fde = table->address, tableSize = table->size; | 912 | for (fde = table->address, tableSize = table->size; |
@@ -895,6 +938,8 @@ int unwind(struct unwind_frame_info *frame) | |||
895 | if (pc >= startLoc && pc < endLoc) | 938 | if (pc >= startLoc && pc < endLoc) |
896 | break; | 939 | break; |
897 | } | 940 | } |
941 | if(!fde) | ||
942 | dprintk(3, "Linear lookup for %lx failed.", pc); | ||
898 | } | 943 | } |
899 | } | 944 | } |
900 | if (cie != NULL) { | 945 | if (cie != NULL) { |
@@ -928,6 +973,8 @@ int unwind(struct unwind_frame_info *frame) | |||
928 | if (ptr >= end || *ptr) | 973 | if (ptr >= end || *ptr) |
929 | cie = NULL; | 974 | cie = NULL; |
930 | } | 975 | } |
976 | if(!cie) | ||
977 | dprintk(1, "CIE unusable (%p,%p).", ptr, end); | ||
931 | ++ptr; | 978 | ++ptr; |
932 | } | 979 | } |
933 | if (cie != NULL) { | 980 | if (cie != NULL) { |
@@ -938,9 +985,11 @@ int unwind(struct unwind_frame_info *frame) | |||
938 | if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) | 985 | if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) |
939 | cie = NULL; | 986 | cie = NULL; |
940 | else if (UNW_PC(frame) % state.codeAlign | 987 | else if (UNW_PC(frame) % state.codeAlign |
941 | || UNW_SP(frame) % sleb128abs(state.dataAlign)) | 988 | || UNW_SP(frame) % sleb128abs(state.dataAlign)) { |
989 | dprintk(1, "Input pointer(s) misaligned (%lx,%lx).", | ||
990 | UNW_PC(frame), UNW_SP(frame)); | ||
942 | return -EPERM; | 991 | return -EPERM; |
943 | else { | 992 | } else { |
944 | retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); | 993 | retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); |
945 | /* skip augmentation */ | 994 | /* skip augmentation */ |
946 | if (((const char *)(cie + 2))[1] == 'z') { | 995 | if (((const char *)(cie + 2))[1] == 'z') { |
@@ -954,6 +1003,8 @@ int unwind(struct unwind_frame_info *frame) | |||
954 | || reg_info[retAddrReg].width != sizeof(unsigned long)) | 1003 | || reg_info[retAddrReg].width != sizeof(unsigned long)) |
955 | cie = NULL; | 1004 | cie = NULL; |
956 | } | 1005 | } |
1006 | if(!cie) | ||
1007 | dprintk(1, "CIE validation failed (%p,%p).", ptr, end); | ||
957 | } | 1008 | } |
958 | if (cie != NULL) { | 1009 | if (cie != NULL) { |
959 | state.cieStart = ptr; | 1010 | state.cieStart = ptr; |
@@ -967,6 +1018,8 @@ int unwind(struct unwind_frame_info *frame) | |||
967 | if ((ptr += augSize) > end) | 1018 | if ((ptr += augSize) > end) |
968 | fde = NULL; | 1019 | fde = NULL; |
969 | } | 1020 | } |
1021 | if(!fde) | ||
1022 | dprintk(1, "FDE validation failed (%p,%p).", ptr, end); | ||
970 | } | 1023 | } |
971 | if (cie == NULL || fde == NULL) { | 1024 | if (cie == NULL || fde == NULL) { |
972 | #ifdef CONFIG_FRAME_POINTER | 1025 | #ifdef CONFIG_FRAME_POINTER |
@@ -1025,8 +1078,10 @@ int unwind(struct unwind_frame_info *frame) | |||
1025 | || state.cfa.reg >= ARRAY_SIZE(reg_info) | 1078 | || state.cfa.reg >= ARRAY_SIZE(reg_info) |
1026 | || reg_info[state.cfa.reg].width != sizeof(unsigned long) | 1079 | || reg_info[state.cfa.reg].width != sizeof(unsigned long) |
1027 | || FRAME_REG(state.cfa.reg, unsigned long) % sizeof(unsigned long) | 1080 | || FRAME_REG(state.cfa.reg, unsigned long) % sizeof(unsigned long) |
1028 | || state.cfa.offs % sizeof(unsigned long)) | 1081 | || state.cfa.offs % sizeof(unsigned long)) { |
1082 | dprintk(1, "Unusable unwind info (%p,%p).", ptr, end); | ||
1029 | return -EIO; | 1083 | return -EIO; |
1084 | } | ||
1030 | /* update frame */ | 1085 | /* update frame */ |
1031 | #ifndef CONFIG_AS_CFI_SIGNAL_FRAME | 1086 | #ifndef CONFIG_AS_CFI_SIGNAL_FRAME |
1032 | if(frame->call_frame | 1087 | if(frame->call_frame |
@@ -1051,6 +1106,8 @@ int unwind(struct unwind_frame_info *frame) | |||
1051 | if (REG_INVALID(i)) { | 1106 | if (REG_INVALID(i)) { |
1052 | if (state.regs[i].where == Nowhere) | 1107 | if (state.regs[i].where == Nowhere) |
1053 | continue; | 1108 | continue; |
1109 | dprintk(1, "Cannot restore register %u (%d).", | ||
1110 | i, state.regs[i].where); | ||
1054 | return -EIO; | 1111 | return -EIO; |
1055 | } | 1112 | } |
1056 | switch(state.regs[i].where) { | 1113 | switch(state.regs[i].where) { |
@@ -1059,8 +1116,11 @@ int unwind(struct unwind_frame_info *frame) | |||
1059 | case Register: | 1116 | case Register: |
1060 | if (state.regs[i].value >= ARRAY_SIZE(reg_info) | 1117 | if (state.regs[i].value >= ARRAY_SIZE(reg_info) |
1061 | || REG_INVALID(state.regs[i].value) | 1118 | || REG_INVALID(state.regs[i].value) |
1062 | || reg_info[i].width > reg_info[state.regs[i].value].width) | 1119 | || reg_info[i].width > reg_info[state.regs[i].value].width) { |
1120 | dprintk(1, "Cannot restore register %u from register %lu.", | ||
1121 | i, state.regs[i].value); | ||
1063 | return -EIO; | 1122 | return -EIO; |
1123 | } | ||
1064 | switch(reg_info[state.regs[i].value].width) { | 1124 | switch(reg_info[state.regs[i].value].width) { |
1065 | #define CASE(n) \ | 1125 | #define CASE(n) \ |
1066 | case sizeof(u##n): \ | 1126 | case sizeof(u##n): \ |
@@ -1070,6 +1130,9 @@ int unwind(struct unwind_frame_info *frame) | |||
1070 | CASES; | 1130 | CASES; |
1071 | #undef CASE | 1131 | #undef CASE |
1072 | default: | 1132 | default: |
1133 | dprintk(1, "Unsupported register size %u (%lu).", | ||
1134 | reg_info[state.regs[i].value].width, | ||
1135 | state.regs[i].value); | ||
1073 | return -EIO; | 1136 | return -EIO; |
1074 | } | 1137 | } |
1075 | break; | 1138 | break; |
@@ -1094,12 +1157,17 @@ int unwind(struct unwind_frame_info *frame) | |||
1094 | CASES; | 1157 | CASES; |
1095 | #undef CASE | 1158 | #undef CASE |
1096 | default: | 1159 | default: |
1160 | dprintk(1, "Unsupported register size %u (%u).", | ||
1161 | reg_info[i].width, i); | ||
1097 | return -EIO; | 1162 | return -EIO; |
1098 | } | 1163 | } |
1099 | break; | 1164 | break; |
1100 | case Value: | 1165 | case Value: |
1101 | if (reg_info[i].width != sizeof(unsigned long)) | 1166 | if (reg_info[i].width != sizeof(unsigned long)) { |
1167 | dprintk(1, "Unsupported value size %u (%u).", | ||
1168 | reg_info[i].width, i); | ||
1102 | return -EIO; | 1169 | return -EIO; |
1170 | } | ||
1103 | FRAME_REG(i, unsigned long) = cfa + state.regs[i].value | 1171 | FRAME_REG(i, unsigned long) = cfa + state.regs[i].value |
1104 | * state.dataAlign; | 1172 | * state.dataAlign; |
1105 | break; | 1173 | break; |
@@ -1111,8 +1179,11 @@ int unwind(struct unwind_frame_info *frame) | |||
1111 | % sizeof(unsigned long) | 1179 | % sizeof(unsigned long) |
1112 | || addr < startLoc | 1180 | || addr < startLoc |
1113 | || addr + sizeof(unsigned long) < addr | 1181 | || addr + sizeof(unsigned long) < addr |
1114 | || addr + sizeof(unsigned long) > endLoc) | 1182 | || addr + sizeof(unsigned long) > endLoc) { |
1183 | dprintk(1, "Bad memory location %lx (%lx).", | ||
1184 | addr, state.regs[i].value); | ||
1115 | return -EIO; | 1185 | return -EIO; |
1186 | } | ||
1116 | switch(reg_info[i].width) { | 1187 | switch(reg_info[i].width) { |
1117 | #define CASE(n) case sizeof(u##n): \ | 1188 | #define CASE(n) case sizeof(u##n): \ |
1118 | probe_kernel_address((u##n *)addr, FRAME_REG(i, u##n)); \ | 1189 | probe_kernel_address((u##n *)addr, FRAME_REG(i, u##n)); \ |
@@ -1120,6 +1191,8 @@ int unwind(struct unwind_frame_info *frame) | |||
1120 | CASES; | 1191 | CASES; |
1121 | #undef CASE | 1192 | #undef CASE |
1122 | default: | 1193 | default: |
1194 | dprintk(1, "Unsupported memory size %u (%u).", | ||
1195 | reg_info[i].width, i); | ||
1123 | return -EIO; | 1196 | return -EIO; |
1124 | } | 1197 | } |
1125 | } | 1198 | } |
@@ -1128,9 +1201,15 @@ int unwind(struct unwind_frame_info *frame) | |||
1128 | } | 1201 | } |
1129 | 1202 | ||
1130 | if (UNW_PC(frame) % state.codeAlign | 1203 | if (UNW_PC(frame) % state.codeAlign |
1131 | || UNW_SP(frame) % sleb128abs(state.dataAlign) | 1204 | || UNW_SP(frame) % sleb128abs(state.dataAlign)) { |
1132 | || (pc == UNW_PC(frame) && sp == UNW_SP(frame))) | 1205 | dprintk(1, "Output pointer(s) misaligned (%lx,%lx).", |
1206 | UNW_PC(frame), UNW_SP(frame)); | ||
1133 | return -EIO; | 1207 | return -EIO; |
1208 | } | ||
1209 | if (pc == UNW_PC(frame) && sp == UNW_SP(frame)) { | ||
1210 | dprintk(1, "No progress (%lx,%lx).", pc, sp); | ||
1211 | return -EIO; | ||
1212 | } | ||
1134 | 1213 | ||
1135 | return 0; | 1214 | return 0; |
1136 | #undef CASES | 1215 | #undef CASES |