def save_ureport(self, db, db_report, ureport, flush=False): db_report.errname = str(ureport["signal"]) db_reportexe = get_reportexe(db, db_report, ureport["executable"]) if db_reportexe is None: db_reportexe = ReportExecutable() db_reportexe.path = ureport["executable"] db_reportexe.report = db_report db_reportexe.count = 0 db.session.add(db_reportexe) db_reportexe.count += 1 bthashes = self._hash_backtrace(ureport["stacktrace"]) if len(bthashes) < 1: raise FafError("Unable to get backtrace hash") bts = filter(None, set(get_backtrace_by_hash(db, b) for b in bthashes)) if len(bts) > 1: raise FafError("Unable to reliably identify backtrace by hash") if len(bts) == 1: db_backtrace = bts.pop() else: new_symbols = {} new_symbolsources = {} db_backtrace = ReportBacktrace() db_backtrace.report = db_report db.session.add(db_backtrace) for bthash in bthashes: db_bthash = ReportBtHash() db_bthash.backtrace = db_backtrace db_bthash.type = "NAMES" db_bthash.hash = bthash db.session.add(db_bthash) tid = 0 for thread in ureport["stacktrace"]: tid += 1 crash = "crash_thread" in thread and thread["crash_thread"] db_thread = ReportBtThread() db_thread.backtrace = db_backtrace db_thread.number = tid db_thread.crashthread = crash db.session.add(db_thread) fid = 0 for frame in thread["frames"]: # OK, this is totally ugly. # Frames may contain inlined functions, that would normally # require shifting all frames by 1 and inserting a new one. # There is no way to do this efficiently with SQL Alchemy # (you need to go one by one and flush after each) so # creating a space for additional frames is a huge speed # optimization. fid += 10 if "build_id" in frame: build_id = frame["build_id"] else: build_id = None if "fingerprint" in frame: fingerprint = frame["fingerprint"] else: fingerprint = None path = os.path.abspath(frame["file_name"]) offset = frame["build_id_offset"] db_symbol = None if "function_name" in frame: norm_path = get_libname(path) db_symbol = \ get_symbol_by_name_path(db, frame["function_name"], norm_path) if db_symbol is None: key = (frame["function_name"], norm_path) if key in new_symbols: db_symbol = new_symbols[key] else: db_symbol = Symbol() db_symbol.name = frame["function_name"] db_symbol.normalized_path = norm_path db.session.add(db_symbol) new_symbols[key] = db_symbol db_symbolsource = get_ssource_by_bpo(db, build_id, path, offset) if db_symbolsource is None: key = (build_id, path, offset) if key in new_symbolsources: db_symbolsource = new_symbolsources[key] else: db_symbolsource = SymbolSource() db_symbolsource.symbol = db_symbol db_symbolsource.build_id = build_id db_symbolsource.path = path db_symbolsource.offset = offset db_symbolsource.hash = fingerprint db.session.add(db_symbolsource) new_symbolsources[key] = db_symbolsource db_frame = ReportBtFrame() db_frame.thread = db_thread db_frame.order = fid db_frame.symbolsource = db_symbolsource db_frame.inlined = False db.session.add(db_frame) if flush: db.session.flush()
def retrace(self, db, task): new_symbols = {} new_symbolsources = {} for bin_pkg, db_ssources in task.binary_packages.items(): self.log_info("Retracing symbols from package {0}" .format(bin_pkg.nvra)) i = 0 for db_ssource in db_ssources: i += 1 self.log_debug("[{0} / {1}] Processing '{2}' @ '{3}'" .format(i, len(db_ssources), ssource2funcname(db_ssource), db_ssource.path)) norm_path = get_libname(db_ssource.path) binary = os.path.join(bin_pkg.unpacked_path, db_ssource.path[1:]) try: address = get_base_address(binary) + db_ssource.offset except FafError as ex: self.log_debug("get_base_address failed: {0}" .format(str(ex))) continue try: debug_path = os.path.join(task.debuginfo.unpacked_path, "usr", "lib", "debug") results = addr2line(binary, address, debug_path) results.reverse() except Exception as ex: self.log_debug("addr2line failed: {0}".format(str(ex))) continue inl_id = 0 while len(results) > 1: inl_id += 1 funcname, srcfile, srcline = results.pop() self.log_debug("Unwinding inlined function '{0}'" .format(funcname)) # hack - we have no offset for inlined symbols # let's use minus source line to avoid collisions offset = -srcline db_ssource_inl = get_ssource_by_bpo(db, db_ssource.build_id, db_ssource.path, offset) if db_ssource_inl is None: key = (db_ssource.build_id, db_ssource.path, offset) if key in new_symbolsources: db_ssource_inl = new_symbolsources[key] else: db_symbol_inl = get_symbol_by_name_path(db, funcname, norm_path) if db_symbol_inl is None: sym_key = (funcname, norm_path) if sym_key in new_symbols: db_symbol_inl = new_symbols[sym_key] else: db_symbol_inl = Symbol() db_symbol_inl.name = funcname db_symbol_inl.normalized_path = norm_path db.session.add(db_symbol_inl) new_symbols[sym_key] = db_symbol_inl db_ssource_inl = SymbolSource() db_ssource_inl.symbol = db_symbol_inl db_ssource_inl.build_id = db_ssource.build_id db_ssource_inl.path = db_ssource.path db_ssource_inl.offset = offset db_ssource_inl.source_path = srcfile db_ssource_inl.line_number = srcline db.session.add(db_ssource_inl) new_symbolsources[key] = db_ssource_inl for db_frame in db_ssource.frames: db_frames = sorted(db_frame.thread.frames, key=lambda f: f.order) idx = db_frames.index(db_frame) if idx > 0: prevframe = db_frame.thread.frames[idx - 1] if (prevframe.inlined and prevframe.symbolsource == db_ssource_inl): continue db_newframe = ReportBtFrame() db_newframe.symbolsource = db_ssource_inl db_newframe.thread = db_frame.thread db_newframe.inlined = True db_newframe.order = db_frame.order - inl_id db.session.add(db_newframe) funcname, srcfile, srcline = results.pop() self.log_debug("Result: {0}".format(funcname)) db_symbol = get_symbol_by_name_path(db, funcname, norm_path) if db_symbol is None: key = (funcname, norm_path) if key in new_symbols: db_symbol = new_symbols[key] else: self.log_debug("Creating new symbol '{0}' @ '{1}'" .format(funcname, db_ssource.path)) db_symbol = Symbol() db_symbol.name = funcname db_symbol.normalized_path = norm_path db.session.add(db_symbol) new_symbols[key] = db_symbol if db_symbol.nice_name is None: db_symbol.nice_name = demangle(funcname) db_ssource.symbol = db_symbol db_ssource.source_path = srcfile db_ssource.line_number = srcline if task.debuginfo.unpacked_path is not None: self.log_debug("Removing {0}".format(task.debuginfo.unpacked_path)) shutil.rmtree(task.debuginfo.unpacked_path, ignore_errors=True) if task.source is not None and task.source.unpacked_path is not None: self.log_debug("Removing {0}".format(task.source.unpacked_path)) shutil.rmtree(task.source.unpacked_path, ignore_errors=True) for bin_pkg in task.binary_packages.keys(): if bin_pkg.unpacked_path is not None: self.log_debug("Removing {0}".format(bin_pkg.unpacked_path)) shutil.rmtree(bin_pkg.unpacked_path, ignore_errors=True)
def retrace_task(db, task): """ Runs the retrace logic on a task and saves results to storage """ for pkg in task["packages"]: for symbolsource in pkg["symbols"]: normalized_path = get_libname(symbolsource.path) # userspace if symbolsource.path.startswith("/"): result = retrace_symbol(symbolsource.path, symbolsource.offset, pkg["unpacked_path"], task["debuginfo"]["unpacked_path"]) # kerneloops else: filename = "vmlinux" if symbolsource.path != "vmlinux": filename = "{0}.ko.debug".format(symbolsource.path) dep = db.session.query(PackageDependency) \ .filter((PackageDependency.name.like("%/{0}".format(filename))) & (PackageDependency.package_id == pkg["package"].id) & (PackageDependency.type == "PROVIDES")) \ .first() if not dep: filename = "{0}.ko.debug".format(symbolsource.path.replace("_", "-")) dep = db.session.query(PackageDependency) \ .filter((PackageDependency.name.like("%/{0}".format(filename))) & (PackageDependency.package_id == pkg["package"].id) & (PackageDependency.type == "PROVIDES")) \ .first() if not dep: logging.debug("{0} not found".format(filename)) continue fmap = task["function_offset_map"] if not symbolsource.path in fmap: logging.debug("Module {0} has no functions associated" \ .format(symbolsource.path)) continue modmap = fmap[symbolsource.path] if not symbolsource.symbol.name in modmap: logging.debug("Function {0} is not present in {1} module" \ .format(symbolsource.symbol.name, symbolsource.path)) continue offset = task["function_offset_map"][symbolsource.path][symbolsource.symbol.name] result = retrace_symbol(dep.name, symbolsource.offset + offset, pkg["unpacked_path"], task["debuginfo"]["unpacked_path"], absolute_offset=True) if result is None: logging.warn("eu-unstrip failed") continue logging.debug("Result: {0}".format(str(result))) if len(result) > 1: inlined_name, inlined_source_path, inlined_line_number = result[1] logging.debug("Separating inlined function {0}" \ .format(inlined_name)) inlined_line_number = int(inlined_line_number) inlined_source = db.session.query(SymbolSource) \ .filter((SymbolSource.build_id == symbolsource.build_id) & (SymbolSource.path == symbolsource.path) & (SymbolSource.offset == -inlined_line_number)) \ .first() if not inlined_source: logging.debug("Creating new SymbolSource") inlined_source = SymbolSource() inlined_source.build_id = symbolsource.build_id inlined_source.path = symbolsource.path inlined_source.offset = -inlined_line_number inlined_source.source_path = inlined_source_path inlined_source.line_number = inlined_line_number db.session.add(inlined_source) inlined_symbol = db.session.query(Symbol) \ .filter((Symbol.name == inlined_name) & (Symbol.normalized_path == normalized_path)) \ .first() if not inlined_symbol: nice_name = cpp_demangle(inlined_name) logging.debug("Creating new Symbol") inlined_symbol = Symbol() inlined_symbol.name = inlined_name if nice_name != inlined_name: logging.debug("Demangled {0} = {1}".format(inlined_name, nice_name)) inlined_symbol.nice_name = nice_name inlined_symbol.normalized_path = normalized_path db.session.add(inlined_symbol) db.session.flush() inlined_source.symbol = inlined_symbol logging.debug("Trying to read source snippet") srcfile = find_source_in_dir(inlined_source.source_path, task["source"]["unpacked_path"], task["source"]["files"]) if srcfile: logging.debug("Reading file '{0}'".format(srcfile)) try: l1, l2, l3 = read_source_snippets(srcfile, inlined_source.line_number) inlined_source.presrcline = l1 inlined_source.srcline = l2 inlined_source.postsrcline = l3 except Exception as ex: logging.debug(str(ex)) else: logging.debug("Source file not found") db.session.flush() total = len(symbolsource.frames) for i in xrange(total): frame = sorted(symbolsource.frames, key=lambda x: (x.backtrace_id, x.order))[i] order = frame.order backtrace = frame.backtrace backtrace_id = backtrace.id frames = backtrace.frames if frames[order - 1].inlined: logging.debug("Already shifted") continue logging.debug("Shifting frames") safe_shift_distance = 2 * len(frames) for f in frames: db.session.expunge(f) db.session.expunge(backtrace) db.session.execute("UPDATE {0} " "SET \"order\" = \"order\" + :safe_distance " "WHERE backtrace_id = :bt_id AND \"order\" >= :from" \ .format(ReportBtFrame.__tablename__), {"safe_distance": safe_shift_distance, "bt_id": backtrace_id, "from": order}) db.session.execute("UPDATE {0} " "SET \"order\" = \"order\" - :safe_distance + 1 " "WHERE backtrace_id = :bt_id AND " " \"order\" >= :from + :safe_distance" \ .format(ReportBtFrame.__tablename__), {"safe_distance": safe_shift_distance, "bt_id": backtrace_id, "from": order}) logging.debug("Creating new ReportBtFrame") newframe = ReportBtFrame() newframe.backtrace_id = backtrace_id newframe.order = order newframe.symbolsource = inlined_source newframe.inlined = True db.session.add(newframe) db.session.flush() symbol_name, symbolsource.source_path, symbolsource.line_number = result[0] if symbol_name == "??": logging.debug("eu-addr2line returned '??', using original " "'{0}' for function name".format(symbolsource.symbol.name)) symbol_name = symbolsource.symbol.name symbol = db.session.query(Symbol) \ .filter((Symbol.name == symbol_name) & (Symbol.normalized_path == normalized_path)) \ .first() if not symbol: nice_name = cpp_demangle(symbol_name) logging.debug("Creating new Symbol") symbol = Symbol() symbol.name = symbol_name if nice_name != symbol_name: logging.debug("Demangled {0} = {1}".format(symbol_name, nice_name)) symbol.nice_name = nice_name symbol.normalized_path = normalized_path db.session.add(symbol) db.session.flush() symbolsource.symbol = symbol logging.debug("Trying to read source snippet") srcfile = find_source_in_dir(symbolsource.source_path, task["source"]["unpacked_path"], task["source"]["files"]) if srcfile: logging.debug("Reading file '{0}'".format(srcfile)) try: l1, l2, l3 = read_source_snippets(srcfile, symbolsource.line_number) symbolsource.presrcline = l1 symbolsource.srcline = l2 symbolsource.postsrcline = l3 except Exception as ex: logging.info(str(ex)) else: logging.debug("Source file not found") # pkg == debuginfo for kerneloops if pkg["unpacked_path"] != task["debuginfo"]["unpacked_path"]: logging.debug("Deleting {0}".format(pkg["unpacked_path"])) # sometimes empty directories are write-only (e.g. ftp dropbox) # they can't be listed, but can be deleted - just ignore the error try: shutil.rmtree(pkg["unpacked_path"]) except Exception as ex: logging.error(str(ex)) logging.debug("Deleting {0}".format(task["source"]["unpacked_path"])) shutil.rmtree(task["source"]["unpacked_path"]) logging.debug("Deleting {0}".format(task["debuginfo"]["unpacked_path"])) shutil.rmtree(task["debuginfo"]["unpacked_path"]) db.session.flush()
def save_ureport(self, db, db_report, ureport, flush=False, count=1): crashframe = ureport["stacktrace"][0] if "special_function" in crashframe: crashfn = "<{0}>".format(crashframe["special_function"]) else: crashfn = crashframe["function_name"] db_report.errname = ureport["exception_name"] db_reportexe = get_reportexe(db, db_report, crashframe["file_name"]) if db_reportexe is None: db_reportexe = ReportExecutable() db_reportexe.report = db_report db_reportexe.path = crashframe["file_name"] db_reportexe.count = 0 db.session.add(db_reportexe) db_reportexe.count += count bthash = self._hash_traceback(ureport["stacktrace"]) if len(db_report.backtraces) < 1: db_backtrace = ReportBacktrace() db_backtrace.report = db_report db_backtrace.crashfn = crashfn db.session.add(db_backtrace) db_bthash = ReportBtHash() db_bthash.type = "NAMES" db_bthash.hash = bthash db_bthash.backtrace = db_backtrace db_thread = ReportBtThread() db_thread.backtrace = db_backtrace db_thread.crashthread = True db.session.add(db_thread) new_symbols = {} new_symbolsources = {} i = 0 for frame in ureport["stacktrace"]: i += 1 if "special_function" in frame: function_name = "<{0}>".format(frame["special_function"]) else: function_name = frame["function_name"] if "special_file" in frame: file_name = "<{0}>".format(frame["special_file"]) else: file_name = frame["file_name"] norm_path = get_libname(file_name) db_symbol = get_symbol_by_name_path(db, function_name, norm_path) if db_symbol is None: key = (function_name, norm_path) if key in new_symbols: db_symbol = new_symbols[key] else: db_symbol = Symbol() db_symbol.name = function_name db_symbol.normalized_path = norm_path db.session.add(db_symbol) new_symbols[key] = db_symbol db_symbolsource = get_symbolsource(db, db_symbol, file_name, frame["file_line"]) if db_symbolsource is None: key = (function_name, file_name, frame["file_line"]) if key in new_symbolsources: db_symbolsource = new_symbolsources[key] else: db_symbolsource = SymbolSource() db_symbolsource.path = file_name db_symbolsource.offset = frame["file_line"] db_symbolsource.source_path = file_name db_symbolsource.symbol = db_symbol if "line_contents" in frame: db_symbolsource.srcline = frame["line_contents"] if "file_line" in frame: db_symbolsource.line_number = frame["file_line"] db.session.add(db_symbolsource) new_symbolsources[key] = db_symbolsource db_frame = ReportBtFrame() db_frame.order = i db_frame.inlined = False db_frame.symbolsource = db_symbolsource db_frame.thread = db_thread db.session.add(db_frame) if flush: db.session.flush()
def retrace_symbol_wrapper(session, source, binary_dir, debuginfo_dir): ''' Handle database references. Delete old symbol with '??' if reference count is 1 and add new symbol if there is no such symbol already. ''' result = retrace_symbol(source.path, source.offset, binary_dir, debuginfo_dir) logging.info('Result: {0}'.format(result)) if result is not None: normalized_path = get_libname(source.path) if len(result) > 1: # <ugly>We have no offset for inlined functions, # so use -1 * line_number</ugly> inlined_name, inlined_source_path, inlined_line_number = result[1] inlined_line_number = int(inlined_line_number) logging.debug("Handling inlined function '{0}'".format(inlined_name)) inlined_source = session.query(SymbolSource) \ .filter((SymbolSource.build_id == source.build_id) & (SymbolSource.path == source.path) & (SymbolSource.offset == -inlined_line_number)) \ .first() if not inlined_source: logging.debug("Creating new SymbolSource") inlined_source = SymbolSource() inlined_source.build_id = source.build_id inlined_source.path = source.path inlined_source.offset = -inlined_line_number inlined_source.line_number = inlined_line_number inlined_source.source_path = inlined_source_path inlined_symbol = session.query(Symbol) \ .filter((Symbol.name == inlined_name) & (Symbol.normalized_path == normalized_path)) \ .first() if not inlined_symbol: logging.debug("Creating new Symbol") inlined_symbol = Symbol() inlined_symbol.name = inlined_name demangled = cpp_demangle(inlined_symbol.name) if demangled != inlined_symbol.name: inlined_symbol.nice_name = demangled inlined_symbol.normalized_path = normalized_path session.add(inlined_symbol) session.flush() inlined_source.symbol_id = inlined_symbol.id session.add(inlined_source) session.flush() else: # although this is strange, it happens # it is probably a bug somewhere # ToDo: fix it if inlined_source.line_number != inlined_line_number: logging.warn("Different line number for same" " build_id+soname+offset") inlined_line_number = inlined_source.line_number if inlined_source.source_path != inlined_source_path: logging.warn("Different source_path for same" " build_id+soname+offset") inlined_source_path = inlined_source.source_path affected = session.query(ReportBtFrame) \ .filter(ReportBtFrame.symbolsource_id == source.id).all() for frame in affected: order = frame.order prevframe = frame.backtrace.frames[order - 1] if prevframe.inlined and \ prevframe.symbolsource_id == inlined_source.id: logging.debug("Already separated, skipping") continue bt_shift_frames(session, frame.backtrace, order) logging.debug("Creating new ReportBtFrame") newframe = ReportBtFrame() newframe.backtrace_id = frame.backtrace_id newframe.order = order newframe.symbolsource_id = inlined_source.id newframe.inlined = True session.add(newframe) session.flush() (symbol_name, source.source_path, source.line_number) = result[0] # Handle eu-addr2line not returing correct function name if symbol_name == '??': symbol_name = source.symbol.name logging.warning('eu-addr2line failed to return function' ' name, using reported name: "{0}"'.format(symbol_name)) # Search for already existing identical symbol. symbol = (session.query(Symbol).filter( (Symbol.name == symbol_name) & (Symbol.normalized_path == normalized_path))).first() possible_duplicates = [] if symbol: # Some symbol has been found. logging.debug('Already got this symbol') source.symbol = symbol for frame in source.frames: possible_duplicates.append(frame.backtrace) else: # Create new symbol. symbol = Symbol() symbol.name = symbol_name demangled = cpp_demangle(symbol.name) if demangled != symbol.name: symbol.nice_name = demangled symbol.normalized_path = normalized_path session.add(symbol) source.symbol = symbol if not is_duplicate_source(session, source): session.add(source) session.flush() # delete unreferenced symbols session.query(Symbol).filter( (Symbol.name == '??') & (Symbol.sources == None)).delete(False) check_duplicate_backtraces(session, possible_duplicates)
def save_ureport(self, db, db_report, ureport, flush=False, count=1): db_report.errname = str(ureport["signal"]) db_reportexe = get_reportexe(db, db_report, ureport["executable"]) if db_reportexe is None: db_reportexe = ReportExecutable() db_reportexe.path = ureport["executable"] db_reportexe.report = db_report db_reportexe.count = 0 db.session.add(db_reportexe) db_reportexe.count += count bthashes = self._hash_backtrace(ureport["stacktrace"]) if not bthashes: raise FafError("Unable to get backtrace hash") if not db_report.backtraces: new_symbols = {} new_symbolsources = {} db_backtrace = ReportBacktrace() db_backtrace.report = db_report db.session.add(db_backtrace) for bthash in bthashes: db_bthash = ReportBtHash() db_bthash.backtrace = db_backtrace db_bthash.type = "NAMES" db_bthash.hash = bthash db.session.add(db_bthash) tid = 0 for thread in ureport["stacktrace"]: tid += 1 crash = "crash_thread" in thread and thread["crash_thread"] db_thread = ReportBtThread() db_thread.backtrace = db_backtrace db_thread.number = tid db_thread.crashthread = crash db.session.add(db_thread) fid = 0 for frame in thread["frames"]: # OK, this is totally ugly. # Frames may contain inlined functions, that would normally # require shifting all frames by 1 and inserting a new one. # There is no way to do this efficiently with SQL Alchemy # (you need to go one by one and flush after each) so # creating a space for additional frames is a huge speed # optimization. fid += 10 if "build_id" in frame: build_id = frame["build_id"] else: build_id = None if "fingerprint" in frame: fingerprint = frame["fingerprint"] else: fingerprint = None path = os.path.abspath(frame["file_name"]) offset = frame["build_id_offset"] db_symbol = None if "function_name" in frame: norm_path = get_libname(path) db_symbol = \ get_symbol_by_name_path(db, frame["function_name"], norm_path) if db_symbol is None: key = (frame["function_name"], norm_path) if key in new_symbols: db_symbol = new_symbols[key] else: db_symbol = Symbol() db_symbol.name = frame["function_name"] db_symbol.normalized_path = norm_path db.session.add(db_symbol) new_symbols[key] = db_symbol db_symbolsource = get_ssource_by_bpo(db, build_id, path, offset) if db_symbolsource is None: key = (build_id, path, offset) if key in new_symbolsources: db_symbolsource = new_symbolsources[key] else: db_symbolsource = SymbolSource() db_symbolsource.symbol = db_symbol db_symbolsource.build_id = build_id db_symbolsource.path = path db_symbolsource.offset = offset db_symbolsource.hash = fingerprint db.session.add(db_symbolsource) new_symbolsources[key] = db_symbolsource db_frame = ReportBtFrame() db_frame.thread = db_thread db_frame.order = fid db_frame.symbolsource = db_symbolsource db_frame.inlined = False db.session.add(db_frame) if flush: db.session.flush()
def save_ureport(self, db, db_report, ureport, flush=False, count=1): crashframe = ureport["stacktrace"][0] if "special_function" in crashframe: crashfn = "<{0}>".format(crashframe["special_function"]) else: crashfn = crashframe["function_name"] db_report.errname = ureport["exception_name"] db_reportexe = get_reportexe(db, db_report, crashframe["file_name"]) if db_reportexe is None: db_reportexe = ReportExecutable() db_reportexe.report = db_report db_reportexe.path = crashframe["file_name"] db_reportexe.count = 0 db.session.add(db_reportexe) db_reportexe.count += count bthash = self._hash_traceback(ureport["stacktrace"]) if not db_report.backtraces: db_backtrace = ReportBacktrace() db_backtrace.report = db_report db_backtrace.crashfn = crashfn db.session.add(db_backtrace) db_bthash = ReportBtHash() db_bthash.type = "NAMES" db_bthash.hash = bthash db_bthash.backtrace = db_backtrace db_thread = ReportBtThread() db_thread.backtrace = db_backtrace db_thread.crashthread = True db.session.add(db_thread) new_symbols = {} new_symbolsources = {} i = 0 for frame in ureport["stacktrace"]: i += 1 if "special_function" in frame: function_name = "<{0}>".format(frame["special_function"]) else: function_name = frame["function_name"] if "special_file" in frame: file_name = "<{0}>".format(frame["special_file"]) else: file_name = frame["file_name"] norm_path = get_libname(file_name) db_symbol = get_symbol_by_name_path(db, function_name, norm_path) if db_symbol is None: key = (function_name, norm_path) if key in new_symbols: db_symbol = new_symbols[key] else: db_symbol = Symbol() db_symbol.name = function_name db_symbol.normalized_path = norm_path db.session.add(db_symbol) new_symbols[key] = db_symbol db_symbolsource = get_symbolsource(db, db_symbol, file_name, frame["file_line"]) if db_symbolsource is None: key = (function_name, file_name, frame["file_line"]) if key in new_symbolsources: db_symbolsource = new_symbolsources[key] else: db_symbolsource = SymbolSource() db_symbolsource.path = file_name db_symbolsource.offset = frame["file_line"] db_symbolsource.source_path = file_name db_symbolsource.symbol = db_symbol if "line_contents" in frame: db_symbolsource.srcline = frame["line_contents"] if "file_line" in frame: db_symbolsource.line_number = frame["file_line"] db.session.add(db_symbolsource) new_symbolsources[key] = db_symbolsource db_frame = ReportBtFrame() db_frame.order = i db_frame.inlined = False db_frame.symbolsource = db_symbolsource db_frame.thread = db_thread db.session.add(db_frame) if flush: db.session.flush()
def add_report(ureport, db, utctime=None, count=1, only_check_if_known=False, return_report=False): if not utctime: utctime = datetime.datetime.utcnow() flip_corebt_if_necessary(ureport) if "component" in ureport: component = get_component(ureport["component"], ureport["os"], db) else: component = guess_component(ureport["installed_package"], ureport["os"], db) if component is None: raise Exception, "Unknown component." for frame in ureport["core_backtrace"]: if not "path" in frame and "executable" in ureport: frame["path"] = ureport["executable"] if ureport["type"].lower() != "kerneloops": frame["path"] = os.path.abspath(frame["path"]) hash_type, hash_hash = get_report_hash(ureport, component.name) # Find a report with matching hash and component. report = ( db.session.query(Report) .join(ReportBacktrace) .join(ReportBtHash) .filter((ReportBtHash.hash == hash_hash) & (ReportBtHash.type == hash_type) & (Report.component == component)) .first() ) if only_check_if_known: # check whether the report has a BZ associated # if it does not, proclaim it unknown if report: reportbz = db.session.query(ReportRhbz).filter(ReportRhbz.report_id == report.id).first() solution = find_ureport_kb_solution(ureport, db, opsys_id=report.component.opsys.id) if not reportbz and not solution: report = None if return_report: return report return bool(report) # Create a new report if not found. if not report: # do not process reports with empty # core-backtrace, except for selinux if ureport["type"].lower() != "selinux" and len(ureport["core_backtrace"]) < 1: raise Exception, "empty core_backtrace" report = Report() report.type = ureport["type"].upper() report.first_occurence = report.last_occurence = utctime report.count = count report.component = component db.session.add(report) report_backtrace = ReportBacktrace() report_backtrace.report = report db.session.add(report_backtrace) report_bthash = ReportBtHash() report_bthash.type = hash_type report_bthash.hash = hash_hash report_bthash.backtrace = report_backtrace db.session.add(report_bthash) # Add frames, symbols, hashes and sources. for frame in get_crash_thread(ureport): report_btframe = ReportBtFrame() report_btframe.backtrace = report_backtrace report_btframe.order = frame["frame"] if not "buildid" in frame: frame["buildid"] = None # Check if there was already such symbol added, but first check # the current session before doing a query. for symbolsource in (x for x in db.session.new if type(x) is SymbolSource): if ( symbolsource.build_id == frame["buildid"] and symbolsource.path == frame["path"] and symbolsource.offset == frame["offset"] ): break else: symbolsource = ( db.session.query(SymbolSource) .filter( (SymbolSource.build_id == frame["buildid"]) & (SymbolSource.path == frame["path"]) & (SymbolSource.offset == frame["offset"]) ) .first() ) # Create a new symbolsource if not found. if not symbolsource: symbolsource = SymbolSource() symbolsource.build_id = frame["buildid"] symbolsource.path = frame["path"] symbolsource.offset = frame["offset"] if ureport["type"].lower() == "python": symbolsource.source_path = frame["path"] symbolsource.line_number = frame["offset"] if "funchash" in frame: symbolsource.hash = frame["funchash"] if "funcname" in frame: normalized_path = get_libname(frame["path"]) for symbol in (x for x in db.session.new if type(x) is Symbol): if symbol.name == frame["funcname"] and symbol.normalized_path == normalized_path: break else: symbol = ( db.session.query(Symbol) .filter((Symbol.name == frame["funcname"]) & (Symbol.normalized_path == normalized_path)) .first() ) # Create a new symbol if not found. if not symbol: symbol = Symbol() symbol.name = frame["funcname"] demangled = cpp_demangle(symbol.name) if demangled != symbol.name: symbol.nice_name = demangled symbol.normalized_path = normalized_path db.session.add(symbol) symbolsource.symbol = symbol db.session.add(symbolsource) report_btframe.symbolsource = symbolsource db.session.add(report_btframe) else: report.count += count if report.last_occurence < utctime: report.last_occurence = utctime elif report.first_occurence > utctime: report.first_occurence = utctime if report.problem: if report.problem.last_occurence < report.last_occurence: report.problem.last_occurence = report.last_occurence if report.problem.first_occurence > report.first_occurence: report.problem.first_occurence = report.first_occurence db.session.flush() if report.type == "KERNELOOPS": if not report.get_lob_fd("oops") and "oops" in ureport: report.save_lob("oops", ureport["oops"]) # Update various stats. opsysrelease = ( db.session.query(OpSysRelease) .join(OpSys) .filter((OpSysRelease.version == ureport["os"]["version"]) & (OpSys.name == ureport["os"]["name"])) .one() ) arch = db.session.query(Arch).filter_by(name=ureport["architecture"]).one() day = utctime.date() week = day - datetime.timedelta(days=day.weekday()) month = day.replace(day=1) stat_map = [ (ReportArch, [("arch", arch)]), (ReportOpSysRelease, [("opsysrelease", opsysrelease)]), (ReportReason, [("reason", ureport["reason"])]), (ReportHistoryMonthly, [("opsysrelease", opsysrelease), ("month", month)]), (ReportHistoryWeekly, [("opsysrelease", opsysrelease), ("week", week)]), (ReportHistoryDaily, [("opsysrelease", opsysrelease), ("day", day)]), ] if "executable" in ureport: stat_map.append((ReportExecutable, [("path", ureport["executable"])])) if "uptime" in ureport: if ureport["uptime"] < 0.1: uptime_exp = -1 else: uptime_exp = int(math.log(ureport["uptime"], 10)) stat_map.append((ReportUptime, [("uptime_exp", uptime_exp)])) # Add the reported package (installed and running). stat_map.append(get_package_stat("CRASHED", ureport, ureport["os"], db)) # Similarly add related packages. if "related_packages" in ureport: for related_package in ureport["related_packages"]: stat_map.append(get_package_stat("RELATED", related_package, ureport["os"], db)) # Add selinux fields to stat_map if "selinux" in ureport: stat_map.append((ReportSelinuxMode, [("mode", ureport["selinux"]["mode"].upper())])) if "context" in ureport["selinux"]: stat_map.append((ReportSelinuxContext, [("context", ureport["selinux"]["context"])])) if "policy_package" in ureport["selinux"]: stat_map.append( get_package_stat( "SELINUX_POLICY", {"installed_package": ureport["selinux"]["policy_package"]}, ureport["os"], db ) ) # Add kernel taint state fields to stat_map. if "kernel_taint_state" in ureport: stat_map.append((ReportKernelTaintState, [("state", ureport["kernel_taint_state"])])) # Create missing stats and increase counters. for table, cols in stat_map: report_stat_query = db.session.query(table).join(Report).filter(Report.id == report.id) for name, value in cols: report_stat_query = report_stat_query.filter(getattr(table, name) == value) report_stat = report_stat_query.first() if not report_stat: report_stat = table() report_stat.report = report for name, value in cols: setattr(report_stat, name, value) report_stat.count = 0 db.session.add(report_stat) report_stat.count += count
def retrace(self, db, task): new_symbols = {} new_symbolsources = {} for bin_pkg, db_ssources in task.binary_packages.items(): self.log_info("Retracing symbols from package {0}".format( bin_pkg.nvra)) i = 0 for db_ssource in db_ssources: i += 1 self.log_debug("[{0} / {1}] Processing '{2}' @ '{3}'".format( i, len(db_ssources), ssource2funcname(db_ssource), db_ssource.path)) norm_path = get_libname(db_ssource.path) binary = os.path.join(bin_pkg.unpacked_path, db_ssource.path[1:]) try: address = get_base_address(binary) + db_ssource.offset except FafError as ex: self.log_debug("get_base_address failed: {0}".format( str(ex))) db_ssource.retrace_fail_count += 1 continue try: debug_path = os.path.join(task.debuginfo.unpacked_path, "usr", "lib", "debug") results = addr2line(binary, address, debug_path) results.reverse() except Exception as ex: # pylint: disable=broad-except self.log_debug("addr2line failed: {0}".format(str(ex))) db_ssource.retrace_fail_count += 1 continue inl_id = 0 while len(results) > 1: inl_id += 1 funcname, srcfile, srcline = results.pop() self.log_debug( "Unwinding inlined function '{0}'".format(funcname)) # hack - we have no offset for inlined symbols # let's use minus source line to avoid collisions offset = -srcline db_ssource_inl = get_ssource_by_bpo( db, db_ssource.build_id, db_ssource.path, offset) if db_ssource_inl is None: key = (db_ssource.build_id, db_ssource.path, offset) if key in new_symbolsources: db_ssource_inl = new_symbolsources[key] else: db_symbol_inl = get_symbol_by_name_path( db, funcname, norm_path) if db_symbol_inl is None: sym_key = (funcname, norm_path) if sym_key in new_symbols: db_symbol_inl = new_symbols[sym_key] else: db_symbol_inl = Symbol() db_symbol_inl.name = funcname db_symbol_inl.normalized_path = norm_path db.session.add(db_symbol_inl) new_symbols[sym_key] = db_symbol_inl db_ssource_inl = SymbolSource() db_ssource_inl.symbol = db_symbol_inl db_ssource_inl.build_id = db_ssource.build_id db_ssource_inl.path = db_ssource.path db_ssource_inl.offset = offset db_ssource_inl.source_path = srcfile db_ssource_inl.line_number = srcline db.session.add(db_ssource_inl) new_symbolsources[key] = db_ssource_inl for db_frame in db_ssource.frames: db_frames = sorted(db_frame.thread.frames, key=lambda f: f.order) idx = db_frames.index(db_frame) if idx > 0: prevframe = db_frame.thread.frames[idx - 1] if (prevframe.inlined and prevframe.symbolsource == db_ssource_inl): continue db_newframe = ReportBtFrame() db_newframe.symbolsource = db_ssource_inl db_newframe.thread = db_frame.thread db_newframe.inlined = True db_newframe.order = db_frame.order - inl_id db.session.add(db_newframe) funcname, srcfile, srcline = results.pop() self.log_debug("Result: {0}".format(funcname)) db_symbol = get_symbol_by_name_path(db, funcname, norm_path) if db_symbol is None: key = (funcname, norm_path) if key in new_symbols: db_symbol = new_symbols[key] else: self.log_debug( "Creating new symbol '{0}' @ '{1}'".format( funcname, db_ssource.path)) db_symbol = Symbol() db_symbol.name = funcname db_symbol.normalized_path = norm_path db.session.add(db_symbol) new_symbols[key] = db_symbol if db_symbol.nice_name is None: db_symbol.nice_name = demangle(funcname) db_ssource.symbol = db_symbol db_ssource.source_path = srcfile db_ssource.line_number = srcline if task.debuginfo.unpacked_path is not None: self.log_debug("Removing {0}".format(task.debuginfo.unpacked_path)) shutil.rmtree(task.debuginfo.unpacked_path, ignore_errors=True) if task.source is not None and task.source.unpacked_path is not None: self.log_debug("Removing {0}".format(task.source.unpacked_path)) shutil.rmtree(task.source.unpacked_path, ignore_errors=True) for bin_pkg in task.binary_packages.keys(): if bin_pkg.unpacked_path is not None: self.log_debug("Removing {0}".format(bin_pkg.unpacked_path)) shutil.rmtree(bin_pkg.unpacked_path, ignore_errors=True)