/** * Finds a section by its name in an ELF file. * @param elf * A libelf handle representing the file. * @param section_name * Name of section to be found. * @param data_dest * Save the resulting elf data pointer here. Cannot be NULL. * @param shdr_dest * Save the section header here. Cannot be NULL. * @param error_message * Will be filled by an error message if the function fails (returns * zero). Caller is responsible for calling free() on the string * pointer. If function succeeds, the pointer is not touched by the * function. * @returns * Zero on error, index of the section on success. */ static unsigned find_elf_section_by_name(Elf *elf, const char *section_name, Elf_Data **data_dest, GElf_Shdr *shdr_dest, char **error_message) { /* Find the string table index */ size_t shdr_string_index; if (0 != elf_getshdrstrndx(elf, &shdr_string_index)) { *error_message = sr_asprintf("elf_getshdrstrndx failed"); return 0; } unsigned section_index = 0; Elf_Scn *section = NULL; while ((section = elf_nextscn(elf, section)) != NULL) { /* Starting index is 1. */ ++section_index; GElf_Shdr shdr; if (gelf_getshdr(section, &shdr) != &shdr) { *error_message = sr_asprintf("gelf_getshdr failed"); return 0; } const char *current_section_name = elf_strptr(elf, shdr_string_index, shdr.sh_name); if (!current_section_name) { *error_message = sr_asprintf("elf_strptr failed"); return 0; } if (0 == strcmp(current_section_name, section_name)) { /* We found the right section! Save the data. */ *data_dest = elf_getdata(section, NULL); if (!*data_dest) { *error_message = sr_asprintf("elf_getdata failed"); return 0; } /* Save the section header. */ *shdr_dest = shdr; return section_index; } } *error_message = sr_asprintf("Section %s not found", section_name); return 0; }
struct sr_rpm_package * sr_abrt_rpm_packages_from_dir(const char *directory, char **error_message) { char *epoch_str = file_contents(directory, "pkg_epoch", error_message); if (!epoch_str) { return NULL; } unsigned long epoch = strtoul(epoch_str, NULL, 10); if (epoch == ULONG_MAX) { *error_message = sr_asprintf("Epoch '%s' is not a number", epoch_str); return NULL; } free(epoch_str); struct sr_rpm_package *packages = sr_rpm_package_new(); packages->epoch = (uint32_t)epoch; packages->name = file_contents(directory, "pkg_name", error_message); packages->version = file_contents(directory, "pkg_version", error_message); packages->release = file_contents(directory, "pkg_release", error_message); packages->architecture = file_contents(directory, "pkg_arch", error_message); packages->role = SR_ROLE_AFFECTED; if (!(packages->name && packages->version && packages->release && packages->architecture)) { sr_rpm_package_free(packages, false); return NULL; } /* Workaround abrt-action-save-kernel-data appending trailing \n for * koopses. */ strip_newline(packages->name); strip_newline(packages->version); strip_newline(packages->release); strip_newline(packages->architecture); char *dso_list_contents = file_contents(directory, "dso_list", error_message); if (dso_list_contents) { struct sr_rpm_package *dso_packages = sr_abrt_parse_dso_list(dso_list_contents); if (dso_packages) { packages = sr_rpm_package_append(packages, dso_packages); packages = sr_rpm_package_sort(packages); packages = sr_rpm_package_uniq(packages); } free(dso_list_contents); } return packages; }
char * sr_core_stacktrace_get_reason(struct sr_core_stacktrace *stacktrace) { char *prog = stacktrace->executable ? stacktrace->executable : "<unknown>"; return sr_asprintf("Program %s was terminated by signal %"PRIu16, prog, stacktrace->signal); }
char * sr_disasm_binary_to_text(struct sr_disasm_state *state, uint64_t start_offset, uint64_t size, char **error_message) { #if HAVE_LIBOPCODES asection *section = state->info.section; if (start_offset < section->vma || (start_offset + size) > section->vma + section->size) { *error_message = sr_asprintf( "Invalid function range: 0x%"PRIx64" - 0x%"PRIx64, start_offset, start_offset + size); return NULL; } char *code = sr_malloc(size); bool success = bfd_get_section_contents(state->bfd_file, state->info.section, code, start_offset - section->vma, size); if (!success) { *error_message = sr_strdup("Failed to get section contents."); return NULL; } struct sr_strbuf *strbuf = sr_strbuf_new(); for (int i = 0; i < size; ++i) { sr_strbuf_append_strf(strbuf, "0x%02x ", (unsigned)code[i]); if ((i + 1) % 12 == 0) sr_strbuf_append_char(strbuf, '\n'); } free(code); return sr_strbuf_free_nobuf(strbuf); #else // HAVE_LIBOPCODES *error_message = sr_asprintf("satyr compiled without libopcodes"); return NULL; #endif // HAVE_LIBOPCODES }
struct sr_core_stacktrace * sr_parse_coredump(const char *coredump_filename, const char *executable_filename, char **error_message) { *error_message = sr_asprintf("satyr is built without unwind support"); return NULL; }
struct sr_core_stacktrace * sr_core_stacktrace_from_core_hook(pid_t thread_id, const char *executable_filename, int signum, char **error_message) { *error_message = sr_asprintf("satyr is built without live process unwind support"); return NULL; }
char * sr_koops_stacktrace_get_reason(struct sr_koops_stacktrace *stacktrace) { char *func = "<unknown>"; char *result; struct sr_koops_stacktrace *copy = sr_koops_stacktrace_dup(stacktrace); sr_normalize_koops_stacktrace(copy); if (copy->frames && copy->frames->function_name) func = copy->frames->function_name; if (copy->frames && copy->frames->module_name) { result = sr_asprintf("Kernel oops in %s [%s]", func, copy->frames->module_name); } else result = sr_asprintf("Kernel oops in %s", func); sr_koops_stacktrace_free(copy); return result; }
char * sr_python_stacktrace_get_reason(struct sr_python_stacktrace *stacktrace) { char *exc = "Unknown error"; char *file = "<unknown>"; uint32_t line = 0; struct sr_python_frame *frame = stacktrace->frames; while (frame && frame->next) frame = frame->next; if (frame) { file = frame->file_name; line = frame->file_line; } if (stacktrace->exception_name) exc = stacktrace->exception_name; return sr_asprintf("%s in %s:%"PRIu32, exc, file, line); }
void sr_normalize_gdb_paired_unknown_function_names(struct sr_gdb_thread *thread1, struct sr_gdb_thread *thread2) { if (!thread1->frames || !thread2->frames) { return; } int i = 0; struct sr_gdb_frame *curr_frame1 = thread1->frames; struct sr_gdb_frame *curr_frame2 = thread2->frames; if (0 == sr_strcmp0(curr_frame1->function_name, "??") && 0 == sr_strcmp0(curr_frame2->function_name, "??") && !(curr_frame1->library_name && curr_frame2->library_name && strcmp(curr_frame1->library_name, curr_frame2->library_name)) && next_functions_similar(curr_frame1, curr_frame2)) { free(curr_frame1->function_name); curr_frame1->function_name = sr_asprintf("__unknown_function_%d", i); free(curr_frame2->function_name); curr_frame2->function_name = sr_asprintf("__unknown_function_%d", i); i++; } struct sr_gdb_frame *prev_frame1 = curr_frame1; struct sr_gdb_frame *prev_frame2 = curr_frame2; curr_frame1 = curr_frame1->next; curr_frame2 = curr_frame2->next; while (curr_frame1) { if (0 == sr_strcmp0(curr_frame1->function_name, "??")) { while (curr_frame2) { if (0 == sr_strcmp0(curr_frame2->function_name, "??") && !(curr_frame1->library_name && curr_frame2->library_name && strcmp(curr_frame1->library_name, curr_frame2->library_name)) && 0 != sr_strcmp0(prev_frame2->function_name, "??") && next_functions_similar(curr_frame1, curr_frame2) && 0 == sr_strcmp0(prev_frame1->function_name, prev_frame2->function_name) && !(prev_frame1->library_name && prev_frame2->library_name && strcmp(prev_frame1->library_name, prev_frame2->library_name))) { free(curr_frame1->function_name); curr_frame1->function_name = sr_asprintf("__unknown_function_%d", i); free(curr_frame2->function_name); curr_frame2->function_name = sr_asprintf("__unknown_function_%d", i); i++; break; } prev_frame2 = curr_frame2; curr_frame2 = curr_frame2->next; } } prev_frame1 = curr_frame1; curr_frame1 = curr_frame1->next; prev_frame2 = thread2->frames; curr_frame2 = prev_frame2->next; } }
struct sr_elf_fde * sr_elf_get_eh_frame(const char *filename, char **error_message) { #ifdef WITH_ELFUTILS /* Open the input file. */ int fd = open(filename, O_RDONLY); if (fd < 0) { *error_message = sr_asprintf("Failed to open file %s: %s", filename, strerror(errno)); return NULL; } /* Initialize libelf on the opened file. */ Elf *elf = elf_begin(fd, ELF_C_READ, NULL); if (!elf) { *error_message = sr_asprintf("Failed to run elf_begin on file %s: %s", filename, elf_errmsg(-1)); close(fd); return NULL; } unsigned char *e_ident = (unsigned char *)elf_getident(elf, NULL); if (!e_ident) { *error_message = sr_asprintf("elf_getident failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } /* Look up the .eh_frame section */ GElf_Shdr shdr; Elf_Data *section_data; char *find_section_error_message; if (!find_elf_section_by_name(elf, ".eh_frame", §ion_data, &shdr, &find_section_error_message)) { *error_message = sr_asprintf("Failed to find .eh_frame section for %s: %s", filename, find_section_error_message); free(find_section_error_message); elf_end(elf); close(fd); return NULL; } /* Get the address at which the executable segment is loaded. If * the .eh_frame addresses are absolute, this is used to convert * them to relative to the beginning of executable segment. We * are looking for the first LOAD segment that is executable, I * hope this is sufficient. */ size_t phnum; if (elf_getphdrnum(elf, &phnum) != 0) { *error_message = sr_asprintf("elf_getphdrnum failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } uint64_t exec_base = -1; int i; for (i = 0; i < phnum; i++) { GElf_Phdr phdr; if (gelf_getphdr(elf, i, &phdr) != &phdr) { *error_message = sr_asprintf("gelf_getphdr failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } if (phdr.p_type == PT_LOAD && phdr.p_flags & PF_X) { exec_base = phdr.p_vaddr; break; } } if (-1 == exec_base) { *error_message = sr_asprintf("Can't determine executable base for %s", filename); elf_end(elf); close(fd); return NULL; } /* We now have a handle to .eh_frame data. We'll use * dwarf_next_cfi to iterate through all FDEs looking for those * matching the addresses we have. * * Some info on .eh_frame can be found at * http://www.airs.com/blog/archives/460 and in DWARF * documentation for .debug_frame. The initial_location and * address_range decoding is 'inspired' by elfutils source. * * @todo If this linear scan is too slow, we can do binary search * on .eh_frame_hdr -- see http://www.airs.com/blog/archives/462 */ struct sr_elf_fde *result = NULL, *last = NULL; struct cie *cie_list = NULL, *cie_list_last = NULL; Dwarf_Off cfi_offset_next = 0; while (true) { Dwarf_CFI_Entry cfi; Dwarf_Off cfi_offset = cfi_offset_next; int ret = dwarf_next_cfi(e_ident, section_data, 1, cfi_offset, &cfi_offset_next, &cfi); if (ret > 0) { /* We're at the end. */ break; } if (ret < 0) { /* Error. If cfi_offset_next was updated, we may skip the * erroneous cfi. */ if (cfi_offset_next > cfi_offset) continue; *error_message = sr_asprintf("dwarf_next_cfi failed for %s: %s", filename, dwarf_errmsg(-1)); cie_free(cie_list); sr_elf_eh_frame_free(result); elf_end(elf); close(fd); return NULL; } if (dwarf_cfi_cie_p(&cfi)) { /* Current CFI is a CIE. We store its offset and FDE encoding * attributes to be used when reading FDEs. */ /* Default FDE encoding (i.e. no R in augmentation string) is * DW_EH_PE_absptr. */ char *cie_error_message; struct cie *cie = read_cie(&cfi, cfi_offset, e_ident, &cie_error_message); if (!cie) { *error_message = sr_asprintf("CIE reading failed for %s: %s", filename, cie_error_message); free(cie_error_message); cie_free(cie_list); sr_elf_eh_frame_free(result); elf_end(elf); close(fd); return NULL; } /* Append the newly parsed CIE to our list of CIEs. */ if (cie_list) { cie_list_last->next = cie; cie_list_last = cie; } else cie_list = cie_list_last = cie; } else { /* Current CFI is an FDE. */ struct cie *cie = cie_list; /* Find the CIE data that we should have saved earlier. */ while (cie) { /* In .eh_frame, CIE_pointer is relative, but libdw converts it * to absolute offset. */ if (cfi.fde.CIE_pointer == cie->cie_offset) break; /* Found. */ cie = cie->next; } if (!cie) { *error_message = sr_asprintf("CIE not found for FDE %jx in %s", (uintmax_t)cfi_offset, filename); cie_free(cie_list); sr_elf_eh_frame_free(result); elf_end(elf); close(fd); return NULL; } /* Read the two numbers we need and if they are PC-relative, * compute the offset from VMA base */ uint64_t initial_location = fde_read_address(cfi.fde.start, cie->ptr_len); uint64_t address_range = fde_read_address(cfi.fde.start + cie->ptr_len, cie->ptr_len); if (cie->pcrel) { /* We need to determine how long is the 'length' (and * consequently CIE id) field of this FDE -- it can be * either 4 or 12 bytes long. */ uint64_t length = fde_read_address(section_data->d_buf + cfi_offset, 4); uint64_t skip = (length == 0xffffffffUL ? 12 : 4); uint64_t mask = (cie->ptr_len == 4 ? 0xffffffffUL : 0xffffffffffffffffUL); initial_location += shdr.sh_offset + cfi_offset + 2 * skip; initial_location &= mask; } else { /* Assuming that not pcrel means absolute address * (what if the file is a library?). Convert to * text-section-start-relative. */ initial_location -= exec_base; } struct sr_elf_fde *fde = sr_malloc(sizeof(struct sr_elf_fde)); fde->exec_base = exec_base; fde->start_address = initial_location; fde->length = address_range; fde->next = NULL; /* Append the newly parsed Frame Description Entry to our * list of FDEs. */ if (result) { last->next = fde; last = fde; } else result = last = fde; } } cie_free(cie_list); elf_end(elf); close(fd); return result; #else /* WITH_ELFUTILS */ *error_message = sr_asprintf("satyr compiled without elfutils"); return NULL; #endif /* WITH_ELFUTILS */ }
static struct cie * read_cie(Dwarf_CFI_Entry *cfi, Dwarf_Off cfi_offset, unsigned char *e_ident, char **error_message) { /* Default FDE encoding (i.e. no R in augmentation string) is * DW_EH_PE_absptr. */ struct cie *cie = sr_mallocz(sizeof(struct cie)); cie->cie_offset = cfi_offset; cie->ptr_len = encoded_size(DW_EH_PE_absptr, e_ident); /* Search the augmentation data for FDE pointer encoding. * Unfortunately, 'P' can come before 'R' (which we are looking * for), so we may have to parse the whole thing. See the * abovementioned blog post for details. */ const char *augmentation = cfi->cie.augmentation; const uint8_t *augmentation_data = cfi->cie.augmentation_data; if (*augmentation == 'z') ++augmentation; while (*augmentation != '\0') { switch (*augmentation) { case 'R': cie->ptr_len = encoded_size(*augmentation_data, e_ident); if (cie->ptr_len != 4 && cie->ptr_len != 8) { *error_message = sr_asprintf("Unknown FDE encoding (CIE %jx)", (uintmax_t)cfi_offset); free(cie); return NULL; } if ((*augmentation_data & 0x70) == DW_EH_PE_pcrel) cie->pcrel = true; return cie; case 'L': ++augmentation_data; break; case 'P': { unsigned size = encoded_size(*augmentation_data, e_ident); if (0 == size) { *error_message = sr_asprintf("Unknown size for personality encoding (CIE %jx)", (uintmax_t)cfi_offset); free(cie); return NULL; } augmentation_data += size + 1; break; } default: *error_message = sr_asprintf("Unknown augmentation char (CIE %jx)", (uintmax_t)cfi_offset); free(cie); return NULL; } ++augmentation; } return cie; }
struct sr_elf_plt_entry * sr_elf_get_procedure_linkage_table(const char *filename, char **error_message) { #ifdef WITH_ELFUTILS /* Open the input file. */ int fd = open(filename, O_RDONLY); if (fd < 0) { *error_message = sr_asprintf("Failed to open file %s: %s", filename, strerror(errno)); return NULL; } /* Initialize libelf on the opened file. */ Elf *elf = elf_begin(fd, ELF_C_READ, NULL); if (!elf) { *error_message = sr_asprintf("Failed to run elf_begin on file %s: %s", filename, elf_errmsg(-1)); close(fd); return NULL; } /* Find the .plt section. */ GElf_Shdr shdr; Elf_Data *plt_data; char *find_section_error_message; size_t plt_section_index = find_elf_section_by_name(elf, ".plt", &plt_data, &shdr, &find_section_error_message); if (0 == plt_section_index) { *error_message = sr_asprintf("Failed to find .plt section for %s: %s", filename, find_section_error_message); free(find_section_error_message); elf_end(elf); close(fd); return NULL; } /* Find the relocation section for .plt (typically .rela.plt), together * with its symbol and string table */ uint64_t plt_base = shdr.sh_addr; Elf_Data *rela_plt_data = NULL; Elf_Data *plt_symbols = NULL; size_t stringtable = 0; Elf_Scn *section = NULL; while ((section = elf_nextscn(elf, section)) != NULL) { if (gelf_getshdr(section, &shdr) != &shdr) { *error_message = sr_asprintf("gelf_getshdr failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } if (shdr.sh_type == SHT_RELA && shdr.sh_info == plt_section_index) { rela_plt_data = elf_getdata(section, NULL); if (!rela_plt_data) { *error_message = sr_asprintf("elf_getdata failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } /* Get symbol section for .rela.plt */ Elf_Scn *symbol_section = elf_getscn(elf, shdr.sh_link); if (!symbol_section) { *error_message = sr_asprintf("elf_getscn failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } plt_symbols = elf_getdata(symbol_section, NULL); if (!plt_symbols) { *error_message = sr_asprintf("elf_getdata failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } /* Get string table for the symbol table. */ if (gelf_getshdr(symbol_section, &shdr) != &shdr) { *error_message = sr_asprintf("gelf_getshdr failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } stringtable = shdr.sh_link; break; } } if (0 == stringtable) { *error_message = sr_asprintf("Unable to read symbol table for .plt for file %s", filename); elf_end(elf); close(fd); return NULL; } /* PLT looks like this (see also AMD64 ABI, page 78): * * Disassembly of section .plt: * * 0000003463e01010 <attr_removef@plt-0x10>: * 3463e01010: ff 35 2a 2c 20 00 pushq 0x202c2a(%rip) <-- here is plt_base * 3463e01016: ff 25 2c 2c 20 00 jmpq *0x202c2c(%rip) each "slot" is 16B wide * 3463e0101c: 0f 1f 40 00 nopl 0x0(%rax) 0-th slot is skipped * * 0000003463e01020 <attr_removef@plt>: * 3463e01020: ff 25 2a 2c 20 00 jmpq *0x202c2a(%rip) * 3463e01026: 68 00 00 00 00 pushq $0x0 <-- this is the number we want * 3463e0102b: e9 e0 ff ff ff jmpq 3463e01010 <_init+0x18> * * 0000003463e01030 <fgetxattr@plt>: * 3463e01030: ff 25 22 2c 20 00 jmpq *0x202c22(%rip) * 3463e01036: 68 01 00 00 00 pushq $0x1 * 3463e0103b: e9 d0 ff ff ff jmpq 3463e01010 <_init+0x18> */ struct sr_elf_plt_entry *result = NULL, *last = NULL; for (unsigned plt_offset = 16; plt_offset < plt_data->d_size; plt_offset += 16) { uint32_t *plt_index = (uint32_t*)(plt_data->d_buf + plt_offset + 7); GElf_Rela rela; if (gelf_getrela(rela_plt_data, *plt_index, &rela) != &rela) { *error_message = sr_asprintf("gelf_getrela failed for %s: %s", filename, elf_errmsg(-1)); sr_elf_procedure_linkage_table_free(result); elf_end(elf); close(fd); return NULL; } GElf_Sym symb; if (gelf_getsym(plt_symbols, GELF_R_SYM(rela.r_info), &symb) != &symb) { *error_message = sr_asprintf("gelf_getsym failed for %s: %s", filename, elf_errmsg(-1)); sr_elf_procedure_linkage_table_free(result); elf_end(elf); close(fd); return NULL; } struct sr_elf_plt_entry *entry = sr_malloc(sizeof(struct sr_elf_plt_entry)); entry->symbol_name = sr_strdup(elf_strptr(elf, stringtable, symb.st_name)); entry->address = (uint64_t)(plt_base + plt_offset); entry->next = NULL; if (result) { last->next = entry; last = entry; } else result = last = entry; } elf_end(elf); close(fd); return result; #else /* WITH_ELFUTILS */ *error_message = sr_asprintf("satyr compiled without elfutils"); return NULL; #endif /* WITH_ELFUTILS */ }
struct sr_disasm_state * sr_disasm_init(const char *file_name, char **error_message) { #if HAVE_LIBOPCODES struct sr_disasm_state *state = sr_malloc(sizeof(struct sr_disasm_state)); state->bfd_file = bfd_openr(file_name, NULL); if (!state->bfd_file) { *error_message = sr_asprintf("Failed to open file %s: %s", file_name, bfd_errmsg(bfd_get_error())); free(state); return NULL; } if (!bfd_check_format(state->bfd_file, bfd_object)) { *error_message = sr_asprintf("Invalid file format of %s: %s", file_name, bfd_errmsg(bfd_get_error())); bfd_close(state->bfd_file); free(state); return NULL; } asection *section = bfd_get_section_by_name(state->bfd_file, ".text"); if (!section) { *error_message = sr_asprintf( "Failed to find .text section in %s: %s", file_name, bfd_errmsg(bfd_get_error())); bfd_close(state->bfd_file); free(state); return NULL; } state->disassembler = disassembler(state->bfd_file); if (!state->disassembler) { *error_message = sr_asprintf( "Unable to find disassembler for %s", file_name); bfd_close(state->bfd_file); free(state); return NULL; } init_disassemble_info(&state->info, NULL, buffer_printf); state->info.arch = bfd_get_arch(state->bfd_file); state->info.mach = bfd_get_mach(state->bfd_file); state->info.buffer_vma = section->vma; state->info.buffer_length = section->size; state->info.section = section; /* TODO: memory error func */ bfd_malloc_and_get_section(state->bfd_file, section, &state->info.buffer); disassemble_init_for_target(&state->info); return state; #else // HAVE_LIBOPCODES *error_message = sr_asprintf("satyr compiled without libopcodes"); return NULL; #endif // HAVE_LIBOPCODES }
struct sr_python_frame * sr_python_frame_parse(const char **input, struct sr_location *location) { const char *local_input = *input; if (0 == sr_skip_string(&local_input, " File \"")) { location->message = sr_asprintf("Frame header not found."); return NULL; } location->column += strlen(" File \""); struct sr_python_frame *frame = sr_python_frame_new(); /* Parse file name */ if (!sr_parse_char_cspan(&local_input, "\"", &frame->file_name)) { location->message = sr_asprintf("Unable to find the '\"' character " "identifying the beginning of file name."); goto fail; } if (strlen(frame->file_name) > 0 && frame->file_name[0] == '<' && frame->file_name[strlen(frame->file_name)-1] == '>') { frame->special_file = true; frame->file_name[strlen(frame->file_name)-1] = '\0'; char *inside = sr_strdup(frame->file_name + 1); free(frame->file_name); frame->file_name = inside; } frame->file_name = anonymize_path(frame->file_name); location->column += strlen(frame->file_name); if (0 == sr_skip_string(&local_input, "\", line ")) { location->message = sr_asprintf("Line separator not found."); goto fail; } location->column += strlen("\", line "); /* Parse line number */ int length = sr_parse_uint32(&local_input, &frame->file_line); if (0 == length) { location->message = sr_asprintf("Line number not found."); goto fail; } location->column += length; if (0 == sr_skip_string(&local_input, ", in ")) { if (local_input[0] != '\n') { location->message = sr_asprintf("Function name separator not found."); goto fail; } /* The last frame of SyntaxError stack trace does not have * function name on its line. For the sake of simplicity, we will * believe that we are dealing with such a frame now. */ frame->function_name = sr_strdup("syntax"); frame->special_function = true; } else { location->column += strlen(", in "); /* Parse function name */ if (!sr_parse_char_cspan(&local_input, "\n", &frame->function_name)) { location->message = sr_asprintf("Unable to find the newline character " "identifying the end of function name."); goto fail; } location->column += strlen(frame->function_name); if (strlen(frame->function_name) > 0 && frame->function_name[0] == '<' && frame->function_name[strlen(frame->function_name)-1] == '>') { frame->special_function = true; frame->function_name[strlen(frame->function_name)-1] = '\0'; char *inside = sr_strdup(frame->function_name + 1); free(frame->function_name); frame->function_name = inside; } } if (sr_skip_char(&local_input, '\n')) sr_location_add(location, 1, 0); /* Parse source code line (optional). */ if (4 == sr_skip_string(&local_input, " ")) { if (sr_parse_char_cspan(&local_input, "\n", &frame->line_contents) && sr_skip_char(&local_input, '\n')) sr_location_add(location, 1, 0); } *input = local_input; return frame; fail: sr_python_frame_free(frame); return NULL; }