def _tar_xz(self, archive_name, archive_dir, filepaths, unlink=True) -> None: archive_path = os.path.join(archive_dir, archive_name) archive_path_tmp = os.path.join(archive_dir, "{0}.tmp".format(archive_name)) tmpdir = get_temp_dir() tmpsubdir = os.path.join(tmpdir, "archive") unlink_paths = list(filepaths) try: os.makedirs(tmpsubdir) except OSError as ex: if ex.errno == errno.EEXIST: raise FafError("The directory '{0}' already exists".format( tmpsubdir)) from ex raise untar = None if os.path.isfile(archive_path): self.log_info("An existing archive found, will merge the contents") untar = self._untar_xz(archive_path) with os.scandir(untar) as iterator: for entry in iterator: if entry.is_dir() and entry.name.endswith("archive"): filepaths = [ os.path.join(entry.path, f) for f in os.listdir(entry.path) ] + filepaths break self.log_info("Creating symlinks") for filepath in filepaths: linkpath = os.path.join(tmpsubdir, os.path.basename(filepath)) # merge - do not overwrite already archived data try: self.log_debug("%s ~> %s", filepath, linkpath) os.symlink(filepath, linkpath) except OSError as ex: if ex.errno != errno.EEXIST: raise self.log_debug("Already exists") self.log_info("Running tar") safe_popen("tar", "chJf", archive_path_tmp, "-C", tmpdir, "archive") os.rename(archive_path_tmp, archive_path) self.log_info("Cleaning up") if untar is not None: shutil.rmtree(untar, ignore_errors=True) if unlink: for path in unlink_paths: os.unlink(path) shutil.rmtree(tmpsubdir)
def _tar_xz(self, archive_name, archive_dir, filepaths, unlink=True): archive_path = os.path.join(archive_dir, archive_name) archive_path_tmp = os.path.join(archive_dir, "{0}.tmp".format(archive_name)) tmpdir = get_temp_dir() tmpsubdir = os.path.join(tmpdir, "archive") unlink_paths = list(filepaths) try: os.makedirs(tmpsubdir) except OSError as ex: if ex.errno == errno.EEXIST: raise FafError("The directory '{0}' already exists" .format(tmpsubdir)) raise untar = None if os.path.isfile(archive_path): self.log_info("An existing archive found, will merge the contents") untar = self._untar_xz(archive_path) for filename in [os.path.join(untar, f) for f in os.listdir(untar)]: if os.path.isdir(filename) and filename.endswith("archive"): filepaths = [os.path.join(filename, f) for f in os.listdir(filename)] + filepaths break self.log_info("Creating symlinks") for filepath in filepaths: linkpath = os.path.join(tmpsubdir, os.path.basename(filepath)) # merge - do not overwrite already archived data try: self.log_debug("{0} ~> {1}".format(filepath, linkpath)) os.symlink(filepath, linkpath) except OSError as ex: if ex.errno != errno.EEXIST: raise self.log_debug("Already exists") self.log_info("Running tar") safe_popen("tar", "chJf", archive_path_tmp, "-C", tmpdir, "archive") os.rename(archive_path_tmp, archive_path) self.log_info("Cleaning up") if untar is not None: shutil.rmtree(untar, ignore_errors=True) if unlink: for path in unlink_paths: os.unlink(path) shutil.rmtree(tmpsubdir)
def get_function_offset_map(files): result = {} for filename in files: modulename = filename.rsplit("/", 1)[1].replace("-", "_") if modulename.endswith(".ko.debug"): modulename = str(modulename[:-9]) if modulename not in result: result[modulename] = {} child = safe_popen("eu-readelf", "-s", filename, encoding="utf-8") if child is None: continue for line in child.stdout.splitlines(): if not "FUNC" in line and not "NOTYPE" in line: continue spl = line.split() try: result[modulename][spl[7].lstrip("_")] = int(spl[1], 16) except IndexError: continue return result
def get_function_offset_map(files): result = {} for filename in files: modulename = filename.rsplit("/", 1)[1].replace("-", "_") if modulename.endswith(".ko.debug"): modulename = str(modulename[:-9]) if modulename not in result: result[modulename] = {} child = safe_popen("eu-readelf", "-s", filename) if child is None: continue for line in child.stdout.splitlines(): if not "FUNC" in line and not "NOTYPE" in line: continue spl = line.split() try: result[modulename][spl[7].lstrip("_")] = int(spl[1], 16) except IndexError: continue return result
def _unstrip( self, cmdline ) -> Union[int, Tuple[List[Tuple[str, Optional[str]]], List[str]]]: build_ids = [] missing = [] self.log_info("Executing eu-unstrip") child = safe_popen("eu-unstrip", "-n", "--core", cmdline.COREDUMP, encoding="utf-8") if child is None: self.log_error("Failed to execute eu-unstrip") return 1 for line in child.stdout.splitlines(): if not all(c in string.printable for c in line): self.log_warn("Skipping line with non-printable characters") self.log_debug(line) continue match = Coredump2Packages.UNSTRIP_LINE_PARSER.match(line) if not match: self.log_warn("Unable to parse line: {0}".format(line)) continue # Build ID if match.group(2): # File if match.group(3).startswith("/"): build_ids.append((match.group(2), match.group(3))) # Name elif match.group(5) != "-": # vDSO if match.group(7): self.log_debug("Skipping vDSO %s", match.group(8)) continue # Deleted if match.group(9): self.log_warn("{} was reported as deleted, " "which means that the file comes from " "a different package version than " "the one installed".format( match.group(5))) continue build_ids.append((match.group(2), match.group(5))) else: build_ids.append((match.group(2), None)) else: missing.append(match.group(3)) return build_ids, missing
def demangle(mangled): """ Demangle C++ symbol name. """ child = safe_popen("c++filt", mangled, encoding="utf-8") if child is None: return None result = child.stdout.strip() if result != mangled: log.debug("Demangled: '%s' ~> '%s'", mangled, result) return result
def demangle(mangled): """ Demangle C++ symbol name. """ child = safe_popen("c++filt", mangled) if child is None: return None result = child.stdout.strip() if result != mangled: log.debug("Demangled: '{0}' ~> '{1}'".format(mangled, result)) return result
def demangle(mangled): """ Demangle C++ symbol name. """ child = safe_popen("c++filt", mangled, encoding="utf-8") if child is None: return None result = child.stdout.strip() if result != mangled: log.debug("Demangled: '{0}' ~> '{1}'".format(mangled, result)) return result
def get_base_address(binary_path): """ Runs eu-unstrip on a binary to get the address used as base for calculating relative offsets. """ child = safe_popen("eu-unstrip", "-n", "-e", binary_path) if child is None: raise FafError("eu-unstrip failed") match = RE_UNSTRIP_BASE_OFFSET.match(child.stdout) if match is None: raise FafError("Unexpected output from eu-unstrip: '{0}'".format( child.stdout)) return int(match.group(1), 16)
def get_base_address(binary_path): """ Runs eu-unstrip on a binary to get the address used as base for calculating relative offsets. """ child = safe_popen("eu-unstrip", "-n", "-e", binary_path, encoding="utf-8") if child is None: raise FafError("eu-unstrip failed") match = RE_UNSTRIP_BASE_OFFSET.match(child.stdout) if match is None: raise FafError("Unexpected output from eu-unstrip: '{0}'" .format(child.stdout)) return int(match.group(1), 16)
def _unstrip(self, cmdline): build_ids = [] missing = [] self.log_info("Executing eu-unstrip") child = safe_popen("eu-unstrip", "-n", "--core", cmdline.COREDUMP, encoding="utf-8") if child is None: self.log_error("Failed to execute eu-unstrip") return 1 for line in child.stdout.splitlines(): if not all(c in string.printable for c in line): self.log_warn("Skipping line with non-printable characters") self.log_debug(line) continue match = Coredump2Packages.UNSTRIP_LINE_PARSER.match(line) if not match: self.log_warn("Unable to parse line: {0}".format(line)) continue # Build ID if match.group(2): # File if match.group(3).startswith("/"): build_ids.append((match.group(2), match.group(3))) # Name elif match.group(5) != "-": # vDSO if match.group(7): self.log_debug("Skipping vDSO {}".format(match.group(8))) continue # Deleted elif match.group(9): self.log_warn("{} was reported as deleted, " "which means that the file comes from " "a different package version than " "the one installed".format(match.group(5))) continue build_ids.append((match.group(2), match.group(5))) else: build_ids.append((match.group(2), None)) else: missing.append(match.group(3)) return build_ids, missing
def addr2line(binary_path, address, debuginfo_dir): """ Calls eu-addr2line on a binary, address and directory with debuginfo. Returns an ordered list of triplets (function name, source file, line no). The last element is always the symbol given to retrace. The elements before are inlined symbols that should be placed above the given symbol (assuming that entry point is on the bottom of the stacktrace). """ result = [] child = safe_popen("eu-addr2line", "--executable", binary_path, "--debuginfo-path", debuginfo_dir, "--functions", str(address)) if child is None: raise FafError("eu-add2line failed") line1, line2 = child.stdout.splitlines() line2_parts = line2.split(":", 1) line2_srcfile = line2_parts[0] line2_srcline = int(line2_parts[1]) match = RE_ADDR2LINE_LINE1.match(line1) if match is None: raise FafError("Unexpected output from eu-addr2line: '{0}'" .format(line1)) if match.group(3) is None: funcname = match.group(1) srcfile = line2_srcfile srcline = line2_srcline else: funcname = match.group(6) srcfile = match.group(4) srcline = int(match.group(5)) result.append((match.group(1), line2_srcfile, line2_srcline)) result.append((funcname, srcfile, srcline)) return result
def addr2line(binary_path, address, debuginfo_dir): """ Calls eu-addr2line on a binary, address and directory with debuginfo. Returns an ordered list of triplets (function name, source file, line no). The last element is always the symbol given to retrace. The elements before are inlined symbols that should be placed above the given symbol (assuming that entry point is on the bottom of the stacktrace). """ result = [] child = safe_popen("eu-addr2line", "--executable", binary_path, "--debuginfo-path", debuginfo_dir, "--functions", str(address)) if child is None: raise FafError("eu-add2line failed") line1, line2 = child.stdout.splitlines() line2_parts = line2.split(":", 1) line2_srcfile = line2_parts[0] line2_srcline = int(line2_parts[1]) match = RE_ADDR2LINE_LINE1.match(line1) if match is None: raise FafError( "Unexpected output from eu-addr2line: '{0}'".format(line1)) if match.group(3) is None: funcname = match.group(1) srcfile = line2_srcfile srcline = line2_srcline else: funcname = match.group(6) srcfile = match.group(4) srcline = int(match.group(5)) result.append((match.group(1), line2_srcfile, line2_srcline)) result.append((funcname, srcfile, srcline)) return result
def addr2line(binary_path, address, debuginfo_dir): """ Calls eu-addr2line on a binary, address and directory with debuginfo. Returns an ordered list of triplets (function name, source file, line no). The last element is always the symbol given to retrace. The elements before are inlined symbols that should be placed above the given symbol (assuming that entry point is on the bottom of the stacktrace). """ result = [] funcname = None srcfile = "??" srcline = 0 # eu-addr2line often finds the symbol if we decrement the address by one. # we try several addresses that maps to no file or to the same source file # and source line as the original address. for addr_enh in range(0, 15): if addr_enh > address: break addr = "0x{0:x}".format(address - addr_enh) child = safe_popen("eu-addr2line", "--executable", binary_path, "--debuginfo-path", debuginfo_dir, "--functions", addr) if child is None: raise FafError("eu-add2line failed") line1, line2 = child.stdout.splitlines() if sys.version_info.major == 3: # Python 3 line1 = line1.decode("utf-8") line2 = line2.decode("utf-8") line2_parts = line2.split(":", 1) line2_srcfile = line2_parts[0] line2_srcline = int(line2_parts[1]) match = RE_ADDR2LINE_LINE1.match(line1) if match is None: raise FafError( "Unexpected output from eu-addr2line: '{0}'".format(line1)) if srcfile != line2_srcfile or srcline != line2_srcline: if srcfile != "??" or srcline != 0: break if match.group(1) == "??": srcfile = line2_srcfile srcline = line2_srcline continue elif match.group(3) is None: funcname = match.group(1) srcfile = line2_srcfile srcline = line2_srcline else: funcname = match.group(6) srcfile = match.group(4) srcline = int(match.group(5)) result.append((match.group(1), line2_srcfile, line2_srcline)) break if funcname is None: raise FafError("eu-addr2line cannot find function name") result.append((funcname, srcfile, srcline)) return result
def run(self, cmdline, db): build_ids = [] missing = [] self.log_info("Executing eu-unstrip") child = safe_popen("eu-unstrip", "-n", "--core", cmdline.COREDUMP) if child is None: self.log_error("Failed to execute eu-unstrip") return 1 for line in child.stdout.splitlines(): match = Coredump2Packages.UNSTRIP_LINE_PARSER.match(line) if not match: self.log_warn("Unable to parse line: {0}".format(line)) continue if match.group(2): if match.group(3).startswith("/"): build_ids.append((match.group(2), match.group(3))) elif (match.group(5) != "-" and not match.group(5).startswith("[")): build_ids.append((match.group(2), match.group(5))) else: build_ids.append((match.group(2), None)) else: missing.append(match.group(3)) self.log_info("Mapping build-ids into debuginfo packages") build_id_maps = {} debuginfos = {} for build_id, soname in build_ids: debug_file = self._build_id_to_debug_file(build_id) db_packages = get_packages_by_file(db, debug_file) db_packages = [p for p in db_packages if p.has_lob("package")] if len(db_packages) < 1: self.log_warn("No debuginfo found for '{0}' ({1})" .format(build_id, soname)) continue else: self.log_debug("Found {0} debuginfo packages for '{1}' ({2}): " "{3}".format(len(db_packages), build_id, soname, [p.nvra() for p in db_packages])) if build_id not in build_id_maps: build_id_maps[build_id] = set() for db_package in db_packages: pkgname = db_package.name pkgnvra = db_package.nvra() build_id_maps[build_id].add(pkgname) if pkgname not in debuginfos: debuginfos[pkgname] = {} if pkgnvra not in debuginfos[pkgname]: debuginfos[pkgname][pkgnvra] = { "count": 0, "package": db_package } debuginfos[pkgname][pkgnvra]["count"] += 1 for build_id, debug_pkgs in build_id_maps.items(): if len(debug_pkgs) > 1: self.log_warn("Debuginfo conflict: '{0}' is provided by {1}" .format(build_id, debug_pkgs)) build_id_maps[build_id] = debug_pkgs.pop() result = set() debuginfo_maps = {} debuginfo_packages = [] for pkgname in sorted(debuginfos): best = { "count": -1, "package": None } for pkgnvra in debuginfos[pkgname]: if debuginfos[pkgname][pkgnvra]["count"] > best["count"]: best = debuginfos[pkgname][pkgnvra] if best["package"]: basename = best["package"].build.base_package_name if basename in Coredump2Packages.SKIP_PACKAGES: self.log_debug("Skipping '{0}'".format(basename)) continue self.log_debug("Picking '{0}' for '{1}' with {2} build_id " "matches".format(best["package"].nvra(), best["package"].name, best["count"])) debuginfo_packages.append(best["package"]) debuginfo_maps[best["package"].name] = best["package"] result.add(best["package"]) else: #paranoia - never happens self.log_warn("Unable to determine best version of '{0}'" .format(pkgname)) self.log_info("Getting binary packages from debuginfos") archs = {} db_build_ids = [dp.build.id for dp in debuginfo_packages] postprocess = set() for build_id, soname in build_ids: if build_id not in build_id_maps: continue if soname is None: if (build_id in build_id_maps and isinstance(build_id_maps[build_id], basestring) and build_id_maps[build_id] in debuginfo_maps): nvra = debuginfo_maps[build_id_maps[build_id]].nvra() self.log_info("No shared object name for '{0}' ({1})" .format(build_id, nvra)) db_build = debuginfo_maps[build_id_maps[build_id]].build postprocess.add(db_build) else: debuginfo_name = build_id_maps[build_id] if debuginfo_name in Coredump2Packages.SKIP_PACKAGES: self.log_debug("Skipping {0}".format(debuginfo_name)) continue db_arch = debuginfo_maps[debuginfo_name].arch abspath = soname.startswith("/") db_packages = get_packages_by_file_builds_arch(db, soname, db_build_ids, db_arch, abspath=abspath) if abspath and len(db_packages) < 1: new_soname = usrmove(soname) db_packages = get_packages_by_file_builds_arch(db, new_soname, db_build_ids, db_arch) if len(db_packages) < 1: self.log_warn("Unable to find binary package for '{0}' " "({1})".format(build_id, soname)) continue for db_package in db_packages: result.add(db_package) arch = db_arch.name if arch not in archs: archs[arch] = 0 archs[arch] += 1 if len(postprocess) > 0 and len(archs) > 0: self.log_info("Post-processing records without shared object name") arch = None archmax = 0 for archname, archcount in archs.items(): if archcount > archmax: archmax = archcount arch = archname self.log_info("Determined architecture: {0}".format(arch)) for db_build in postprocess: basename = db_build.base_package_name if basename in Coredump2Packages.SKIP_PACKAGES: self.log_info("Skipping {0}".format(basename)) continue for db_package in db_build.packages: if db_package.arch.name == arch: self.log_debug("Picking {0} for {1}" .format(db_package.nvra(), basename)) result.add(db_package) link = None tmpdir = None if cmdline.symlink_dir: tmpdir = tempfile.mkdtemp(dir=cmdline.symlink_dir) link = os.symlink elif cmdline.hardlink_dir: tmpdir = tempfile.mkdtemp(dir=cmdline.hardlink_dir) link = os.link for db_package in result: if link is None: print(db_package.nvra()) continue path_from = db_package.get_lob_path("package") path_to = os.path.join(tmpdir, "{0}.rpm".format(db_package.nvra())) try: link(path_from, path_to) except OSError: if cmdline.no_copy: continue shutil.copy2(path_from, path_to) if tmpdir is not None: print tmpdir
def run(self, cmdline, db): build_ids = [] missing = [] self.log_info("Executing eu-unstrip") child = safe_popen("eu-unstrip", "-n", "--core", cmdline.COREDUMP) if child is None: self.log_error("Failed to execute eu-unstrip") return 1 for line in child.stdout.splitlines(): match = Coredump2Packages.UNSTRIP_LINE_PARSER.match(line) if not match: self.log_warn("Unable to parse line: {0}".format(line)) continue if not all(c in string.printable for c in line): self.log_warn("Skipping line with non-printable characters") self.log_debug(line) continue if match.group(2): if match.group(3).startswith("/"): build_ids.append((match.group(2), match.group(3))) elif (match.group(5) != "-" and not match.group(5).startswith("[")): build_ids.append((match.group(2), match.group(5))) else: build_ids.append((match.group(2), None)) else: missing.append(match.group(3)) self.log_info("Mapping build-ids into debuginfo packages") build_id_maps = {} debuginfos = {} for build_id, soname in build_ids: debug_file = self._build_id_to_debug_file(build_id) db_packages = get_packages_by_file(db, debug_file) db_packages = [p for p in db_packages if p.has_lob("package")] if len(db_packages) < 1: self.log_warn("No debuginfo found for '{0}' ({1})".format( build_id, soname)) continue else: self.log_debug("Found {0} debuginfo packages for '{1}' ({2}): " "{3}".format(len(db_packages), build_id, soname, [p.nvra() for p in db_packages])) if build_id not in build_id_maps: build_id_maps[build_id] = set() for db_package in db_packages: pkgname = db_package.name pkgnvra = db_package.nvra() build_id_maps[build_id].add(pkgname) if pkgname not in debuginfos: debuginfos[pkgname] = {} if pkgnvra not in debuginfos[pkgname]: debuginfos[pkgname][pkgnvra] = { "count": 0, "package": db_package } debuginfos[pkgname][pkgnvra]["count"] += 1 for build_id, debug_pkgs in build_id_maps.items(): if len(debug_pkgs) > 1: self.log_warn( "Debuginfo conflict: '{0}' is provided by {1}".format( build_id, debug_pkgs)) build_id_maps[build_id] = debug_pkgs.pop() result = set() debuginfo_maps = {} debuginfo_packages = [] for pkgname in sorted(debuginfos): best = {"count": -1, "package": None} for pkgnvra in debuginfos[pkgname]: if debuginfos[pkgname][pkgnvra]["count"] > best["count"]: best = debuginfos[pkgname][pkgnvra] if best["package"]: basename = best["package"].build.base_package_name if basename in Coredump2Packages.SKIP_PACKAGES: self.log_debug("Skipping '{0}'".format(basename)) continue self.log_debug("Picking '{0}' for '{1}' with {2} build_id " "matches".format(best["package"].nvra(), best["package"].name, best["count"])) debuginfo_packages.append(best["package"]) debuginfo_maps[best["package"].name] = best["package"] result.add(best["package"]) else: #paranoia - never happens self.log_warn( "Unable to determine best version of '{0}'".format( pkgname)) self.log_info("Getting binary packages from debuginfos") archs = {} db_build_ids = [dp.build.id for dp in debuginfo_packages] postprocess = set() for build_id, soname in build_ids: if build_id not in build_id_maps: continue if soname is None: if (build_id in build_id_maps and isinstance( build_id_maps[build_id], six.string_types) and build_id_maps[build_id] in debuginfo_maps): nvra = debuginfo_maps[build_id_maps[build_id]].nvra() self.log_info( "No shared object name for '{0}' ({1})".format( build_id, nvra)) db_build = debuginfo_maps[build_id_maps[build_id]].build postprocess.add(db_build) else: debuginfo_name = build_id_maps[build_id] if debuginfo_name in Coredump2Packages.SKIP_PACKAGES: self.log_debug("Skipping {0}".format(debuginfo_name)) continue db_arch = debuginfo_maps[debuginfo_name].arch abspath = soname.startswith("/") db_packages = get_packages_by_file_builds_arch(db, soname, db_build_ids, db_arch, abspath=abspath) if abspath and len(db_packages) < 1: new_soname = usrmove(soname) db_packages = get_packages_by_file_builds_arch( db, new_soname, db_build_ids, db_arch) if len(db_packages) < 1: self.log_warn("Unable to find binary package for '{0}' " "({1})".format(build_id, soname)) continue for db_package in db_packages: result.add(db_package) arch = db_arch.name if arch not in archs: archs[arch] = 0 archs[arch] += 1 if len(postprocess) > 0 and len(archs) > 0: self.log_info("Post-processing records without shared object name") arch = None archmax = 0 for archname, archcount in archs.items(): if archcount > archmax: archmax = archcount arch = archname self.log_info("Determined architecture: {0}".format(arch)) for db_build in postprocess: basename = db_build.base_package_name if basename in Coredump2Packages.SKIP_PACKAGES: self.log_info("Skipping {0}".format(basename)) continue for db_package in db_build.packages: if db_package.arch.name == arch: self.log_debug("Picking {0} for {1}".format( db_package.nvra(), basename)) result.add(db_package) link = None tmpdir = None if cmdline.symlink_dir: tmpdir = tempfile.mkdtemp(dir=cmdline.symlink_dir) link = os.symlink elif cmdline.hardlink_dir: tmpdir = tempfile.mkdtemp(dir=cmdline.hardlink_dir) link = os.link for db_package in result: if link is None: print(db_package.nvra()) continue path_from = db_package.get_lob_path("package") path_to = os.path.join(tmpdir, "{0}.rpm".format(db_package.nvra())) try: link(path_from, path_to) except OSError: if cmdline.no_copy: continue shutil.copy2(path_from, path_to) if tmpdir is not None: print(tmpdir)
def _untar_xz(self, archive): tmpdir = tempfile.mkdtemp(dir=get_temp_dir(), prefix=os.path.basename(archive)) safe_popen("tar", "xJf", archive, "-C", tmpdir) return tmpdir
def addr2line(binary_path, address, debuginfo_dir): """ Calls eu-addr2line on a binary, address and directory with debuginfo. Returns an ordered list of triplets (function name, source file, line no). The last element is always the symbol given to retrace. The elements before are inlined symbols that should be placed above the given symbol (assuming that entry point is on the bottom of the stacktrace). """ result = [] funcname = None srcfile = "??" srcline = 0 # eu-addr2line often finds the symbol if we decrement the address by one. # we try several addresses that maps to no file or to the same source file # and source line as the original address. for addr_enh in range(0, 15): if addr_enh > address: break addr = "0x{0:x}".format(address - addr_enh) child = safe_popen("eu-addr2line", "--executable", binary_path, "--debuginfo-path", debuginfo_dir, "--functions", addr, encoding="utf-8") if child is None: raise FafError("eu-add2line failed") line1, line2 = child.stdout.splitlines() # format of the line2 is filename:lineno[:columnno] line2_parts = line2.split(":") line2_srcfile = line2_parts[0] line2_srcline = int(line2_parts[1]) match = RE_ADDR2LINE_LINE1.match(line1) if match is None: raise FafError("Unexpected output from eu-addr2line: '{0}'" .format(line1)) if srcfile != line2_srcfile or srcline != line2_srcline: if srcfile != "??" or srcline != 0: break if match.group(1) == "??": srcfile = line2_srcfile srcline = line2_srcline continue elif match.group(3) is None: funcname = match.group(1) srcfile = line2_srcfile srcline = line2_srcline else: funcname = match.group(6) srcfile = match.group(4) srcline = int(match.group(5)) result.append((match.group(1), line2_srcfile, line2_srcline)) break if funcname is None: raise FafError("eu-addr2line cannot find function name") result.append((funcname, srcfile, srcline)) return result