def retrace(self, db, task): new_symbols = {} new_symbolsources = {} debug_paths = set(os.path.join(task.debuginfo.unpacked_path, fname[1:]) for fname in task.debuginfo.debug_files) if task.debuginfo.debug_files is not None: db_debug_pkg = task.debuginfo.db_package if db_debug_pkg.has_lob("offset_map"): with db_debug_pkg.get_lob_fd("offset_map") as fd: offset_map = pickle.load(fd) else: offset_map = get_function_offset_map(debug_paths) db_debug_pkg.save_lob("offset_map", pickle.dumps(offset_map)) else: offset_map = {} for bin_pkg, db_ssources in task.binary_packages.items(): i = 0 for db_ssource in db_ssources: i += 1 module = db_ssource.path self.log_info(u"[{0} / {1}] Processing '{2}' @ '{3}'" .format(i, len(db_ssources), db_ssource.symbol.name, module)) if db_ssource.path == "vmlinux": address = db_ssource.offset if address < 0: address += (1 << 64) else: if module not in offset_map: self.log_debug("Module '{0}' not found in package '{1}'" .format(module, task.debuginfo.nvra)) db_ssource.retrace_fail_count += 1 continue module_map = offset_map[module] symbol_name = db_ssource.symbol.name if symbol_name not in module_map: symbol_name = symbol_name.lstrip("_") if symbol_name not in module_map: self.log_debug("Function '{0}' not found in module " "'{1}'".format(db_ssource.symbol.name, module)) db_ssource.retrace_fail_count += 1 continue address = module_map[symbol_name] + db_ssource.func_offset debug_dir = os.path.join(task.debuginfo.unpacked_path, "usr", "lib", "debug") debug_path = self._get_debug_path(db, module, task.debuginfo.db_package) if debug_path is None: db_ssource.retrace_fail_count += 1 continue try: abspath = os.path.join(task.debuginfo.unpacked_path, debug_path[1:]) results = addr2line(abspath, address, debug_dir) results.reverse() except FafError as ex: 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, module) if db_symbol_inl is None: sym_key = (funcname, module) 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 = module 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 = module 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, module) if db_symbol is None: key = (funcname, module) if key in new_symbols: db_symbol = new_symbols[key] else: self.log_debug("Creating new symbol '{0}' @ '{1}'" .format(funcname, module)) db_symbol = Symbol() db_symbol.name = funcname db_symbol.normalized_path = module 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 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)
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 save_ureport(self, db, db_report, ureport, flush=False, count=1): bthash1 = self._hash_koops(ureport["frames"], skip_unreliable=False) bthash2 = self._hash_koops(ureport["frames"], skip_unreliable=True) if len(db_report.backtraces) < 1: db_backtrace = ReportBacktrace() db_backtrace.report = db_report db.session.add(db_backtrace) db_thread = ReportBtThread() db_thread.backtrace = db_backtrace db_thread.crashthread = True db.session.add(db_thread) db_bthash1 = ReportBtHash() db_bthash1.backtrace = db_backtrace db_bthash1.hash = bthash1 db_bthash1.type = "NAMES" db.session.add(db_bthash1) if bthash2 is not None and bthash1 != bthash2: db_bthash2 = ReportBtHash() db_bthash2.backtrace = db_backtrace db_bthash2.hash = bthash2 db_bthash2.type = "NAMES" db.session.add(db_bthash2) new_symbols = {} new_symbolsources = {} i = 0 for frame in ureport["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. i += 10 # nah, another hack, deals with wrong parsing if frame["function_name"].startswith("0x"): continue if not "module_name" in frame: module = "vmlinux" else: module = frame["module_name"] db_symbol = get_symbol_by_name_path(db, frame["function_name"], module) if db_symbol is None: key = (frame["function_name"], module) if key in new_symbols: db_symbol = new_symbols[key] else: db_symbol = Symbol() db_symbol.name = frame["function_name"] db_symbol.normalized_path = module db.session.add(db_symbol) new_symbols[key] = db_symbol # this doesn't work well. on 64bit, kernel maps to # the end of address space (64bit unsigned), but in # postgres bigint is 64bit signed and can't save # the value - let's just map it to signed if frame["address"] >= (1 << 63): address = frame["address"] - (1 << 64) else: address = frame["address"] db_symbolsource = get_ssource_by_bpo(db, ureport["version"], module, address) if db_symbolsource is None: key = (ureport["version"], module, address) if key in new_symbolsources: db_symbolsource = new_symbolsources[key] else: db_symbolsource = SymbolSource() db_symbolsource.path = module db_symbolsource.offset = address db_symbolsource.func_offset = frame["function_offset"] db_symbolsource.symbol = db_symbol db_symbolsource.build_id = ureport["version"] db.session.add(db_symbolsource) new_symbolsources[key] = db_symbolsource db_frame = ReportBtFrame() db_frame.thread = db_thread db_frame.order = i db_frame.symbolsource = db_symbolsource db_frame.inlined = False db_frame.reliable = frame["reliable"] db.session.add(db_frame) for taintflag in ureport["taint_flags"]: db_taintflag = get_taint_flag_by_ureport_name(db, taintflag) if db_taintflag is None: self.log_warn("Skipping unsupported taint flag '{0}'" .format(taintflag)) continue db_bttaintflag = ReportBtTaintFlag() db_bttaintflag.backtrace = db_backtrace db_bttaintflag.taintflag = db_taintflag db.session.add(db_bttaintflag) if "modules" in ureport: new_modules = {} # use set() to remove duplicates for module in set(ureport["modules"]): idx = module.find("(") if idx >= 0: module = module[:idx] db_module = get_kernelmodule_by_name(db, module) if db_module is None: if module in new_modules: db_module = new_modules[module] else: db_module = KernelModule() db_module.name = module db.session.add(db_module) new_modules[module] = db_module db_btmodule = ReportBtKernelModule() db_btmodule.kernelmodule = db_module db_btmodule.backtrace = db_backtrace db.session.add(db_btmodule) # do not overwrite an existing oops if not db_report.has_lob("oops"): # do not append here, but create a new dict # we only want save_ureport_post_flush process the most # recently saved report self.add_lob = {db_report: ureport["raw_oops"].encode("utf-8")} if flush: db.session.flush()
def save_ureport(self, db, db_report, ureport, flush=False, count=1): # at the moment we only send crash thread # we may need to identify the crash thread in the future crashthread = ureport["threads"][0] crashfn = None for frame in crashthread["frames"]: if not frame["is_exception"]: crashfn = frame["name"] break if crashfn is not None and "." in crashfn: crashfn = crashfn.rsplit(".", 1)[1] errname = None for frame in crashthread["frames"]: if frame["is_exception"]: errname = frame["name"] break if "." in errname: errname = errname.rsplit(".", 1)[1] db_report.errname = errname bthash = self._hash_backtrace(ureport["threads"]) 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 new_symbols = {} new_symbolsources = {} j = 0 for thread in ureport["threads"]: j += 1 db_thread = ReportBtThread() db_thread.backtrace = db_backtrace db_thread.crashthread = thread == crashthread db_thread.number = j db.session.add(db_thread) i = 0 for frame in thread["frames"]: i += 1 function_name = frame["name"] if "class_path" in frame: file_name = frame["class_path"] elif frame["is_exception"]: file_name = JavaProblem.exception elif frame["is_native"]: file_name = JavaProblem.native else: file_name = JavaProblem.unknown if "file_line" in frame: file_line = frame["file_line"] else: file_line = 0 db_symbol = get_symbol_by_name_path(db, function_name, file_name) if db_symbol is None: key = (function_name, file_name) if key in new_symbols: db_symbol = new_symbols[key] else: db_symbol = Symbol() db_symbol.name = function_name db_symbol.normalized_path = file_name db.session.add(db_symbol) new_symbols[key] = db_symbol db_symbolsource = get_symbolsource(db, db_symbol, file_name, file_line) if db_symbolsource is None: key = (function_name, file_name, file_line) if key in new_symbolsources: db_symbolsource = new_symbolsources[key] else: db_symbolsource = SymbolSource() db_symbolsource.path = file_name db_symbolsource.offset = file_line if "file_name" in frame: db_symbolsource.source_path = frame["file_name"] db_symbolsource.line_number = file_line db_symbolsource.symbol = db_symbol 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 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 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 run(self, cmdline, db): if len(cmdline.problemtype) < 1: ptypes = problemtypes.keys() else: ptypes = cmdline.problemtype for ptype in ptypes: if not ptype in problemtypes: self.log_warn("Problem type '{0}' is not supported" .format(ptype)) continue problemplugin = problemtypes[ptype] self.log_info("Processing '{0}' problem type" .format(problemplugin.nice_name)) db_ssources = problemplugin.get_ssources_for_retrace( db, yield_per=cmdline.batch) if len(db_ssources) < 1: continue i = 0 batch = [] db_batch = [] for db_ssource in db_ssources: i += 1 self.log_info("Processing symbol {0}/{1}" .format(i, len(db_ssources))) req_data = { "build_id": db_ssource.build_id, "path": db_ssource.path, "offset": db_ssource.offset, "type": ptype, } batch.append(req_data) db_batch.append(db_ssource) if len(batch) >= cmdline.batch or i == len(db_ssources): self.log_info("Sending request...") r = requests.post( self.remote_url, data=json.dumps(batch), params={"create_symbol_auth": self.auth_key}, headers={"content-type": "application/json"} ) if r.status_code == requests.codes.ok: res_data = r.json() if len(res_data) != len(batch): self.log_warn("Response length mismatch.") batch = [] db_batch = [] continue new_db_symbols = {} for j in xrange(len(res_data)): data = res_data[j] if data.get("error", False): self.log_info(data["error"]) continue db_ssource = db_batch[j] ssource = data["SymbolSource"] symbol = data["Symbol"] db_ssource.build_id = ssource["build_id"] db_ssource.path = ssource["path"] db_ssource.offset = ssource["offset"] db_ssource.func_offset = ssource["func_offset"] db_ssource.hash = ssource["hash"] db_ssource.source_path = ssource["source_path"] db_ssource.line_number = ssource["line_number"] db_symbol = get_symbol_by_name_path(db, symbol["name"], symbol["normalized_path"]) if db_symbol is None: db_symbol = new_db_symbols.get((symbol["name"], symbol["normalized_path"]), None) if db_symbol is None: db_symbol = Symbol() db.session.add(db_symbol) new_db_symbols[(symbol["name"], symbol["normalized_path"])] = db_symbol db_symbol.name = symbol["name"] db_symbol.nice_name = symbol["nice_name"] db_symbol.normalized_path = symbol["normalized_path"] db_ssource.symbol = db_symbol self.log_info("Symbol saved.") db.session.flush() batch = [] db_batch = []
def save_ureport(self, db, db_report, ureport, flush=False, count=1) -> None: # at the moment we only send crash thread # we may need to identify the crash thread in the future crashthread = ureport["threads"][0] crashfn = None for frame in crashthread["frames"]: if not frame["is_exception"]: crashfn = frame["name"] break if crashfn is not None and "." in crashfn: crashfn = crashfn.rsplit(".", 1)[1] errname = None for frame in crashthread["frames"]: if frame["is_exception"]: errname = frame["name"] break if "." in errname: errname = errname.rsplit(".", 1)[1] db_report.errname = errname bthash = self._hash_backtrace(ureport["threads"]) 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 new_symbols = {} new_symbolsources = {} j = 0 for thread in ureport["threads"]: j += 1 db_thread = ReportBtThread() db_thread.backtrace = db_backtrace db_thread.crashthread = thread == crashthread db_thread.number = j db.session.add(db_thread) i = 0 for frame in thread["frames"]: i += 1 function_name = frame["name"] if "class_path" in frame: file_name = frame["class_path"] elif frame["is_exception"]: file_name = JavaProblem.exception elif frame["is_native"]: file_name = JavaProblem.native else: file_name = JavaProblem.unknown if "file_line" in frame: file_line = frame["file_line"] else: file_line = 0 db_symbol = get_symbol_by_name_path( db, function_name, file_name) if db_symbol is None: key = (function_name, file_name) if key in new_symbols: db_symbol = new_symbols[key] else: db_symbol = Symbol() db_symbol.name = function_name db_symbol.normalized_path = file_name db.session.add(db_symbol) new_symbols[key] = db_symbol db_symbolsource = get_symbolsource(db, db_symbol, file_name, file_line) if db_symbolsource is None: key = (function_name, file_name, file_line) if key in new_symbolsources: db_symbolsource = new_symbolsources[key] else: db_symbolsource = SymbolSource() db_symbolsource.path = file_name db_symbolsource.offset = file_line if "file_name" in frame: db_symbolsource.source_path = frame[ "file_name"] db_symbolsource.line_number = file_line db_symbolsource.symbol = db_symbol 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 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 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: 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)
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 len(bthashes) < 1: raise FafError("Unable to get backtrace hash") if len(db_report.backtraces) < 1: 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 run(self, cmdline, db) -> None: if not cmdline.problemtype: ptypes = list(problemtypes.keys()) else: ptypes = cmdline.problemtype for ptype in ptypes: if not ptype in problemtypes: self.log_warn( "Problem type '{0}' is not supported".format(ptype)) continue problemplugin = problemtypes[ptype] self.log_info("Processing '{0}' problem type".format( problemplugin.nice_name)) db_ssources = problemplugin.get_ssources_for_retrace( db, yield_per=cmdline.batch) if not db_ssources: continue batch = [] db_batch = [] for i, db_ssource in enumerate(db_ssources, start=1): self.log_info("Processing symbol {0}/{1}".format( i, len(db_ssources))) req_data = { "build_id": db_ssource.build_id, "path": db_ssource.path, "offset": db_ssource.offset, "type": ptype, } batch.append(req_data) db_batch.append(db_ssource) if len(batch) >= cmdline.batch or i == len(db_ssources): self.log_info("Sending request...") r = requests.post( self.remote_url, data=json.dumps(batch), params={"create_symbol_auth": self.auth_key}, headers={"content-type": "application/json"}) if r.status_code == requests.codes['ok']: res_data = r.json() if len(res_data) != len(batch): self.log_warn("Response length mismatch.") batch = [] db_batch = [] continue new_db_symbols = {} for j, data in enumerate(res_data): if data.get("error", False): self.log_info(data["error"]) continue db_ssource = db_batch[j] ssource = data["SymbolSource"] symbol = data["Symbol"] db_ssource.build_id = ssource["build_id"] db_ssource.path = ssource["path"] db_ssource.offset = ssource["offset"] db_ssource.func_offset = ssource["func_offset"] db_ssource.hash = ssource["hash"] db_ssource.source_path = ssource["source_path"] db_ssource.line_number = ssource["line_number"] db_symbol = get_symbol_by_name_path( db, symbol["name"], symbol["normalized_path"]) if db_symbol is None: db_symbol = new_db_symbols.get( (symbol["name"], symbol["normalized_path"]), None) if db_symbol is None: db_symbol = Symbol() db.session.add(db_symbol) new_db_symbols[( symbol["name"], symbol["normalized_path"])] = db_symbol db_symbol.name = symbol["name"] db_symbol.nice_name = symbol["nice_name"] db_symbol.normalized_path = symbol[ "normalized_path"] db_ssource.symbol = db_symbol self.log_info("Symbol saved.") db.session.flush() batch = [] db_batch = []
def retrace(self, db, task): new_symbols = {} new_symbolsources = {} debug_paths = set( os.path.join(task.debuginfo.unpacked_path, fname[1:]) for fname in task.debuginfo.debug_files) if task.debuginfo.debug_files is not None: db_debug_pkg = task.debuginfo.db_package if db_debug_pkg.has_lob("offset_map"): with db_debug_pkg.get_lob_fd("offset_map") as fd: offset_map = pickle.load(fd) else: offset_map = get_function_offset_map(debug_paths) db_debug_pkg.save_lob("offset_map", pickle.dumps(offset_map)) else: offset_map = {} for _, db_ssources in task.binary_packages.items(): i = 0 for db_ssource in db_ssources: i += 1 module = db_ssource.path self.log_info(u"[{0} / {1}] Processing '{2}' @ '{3}'".format( i, len(db_ssources), db_ssource.symbol.name, module)) if db_ssource.path == "vmlinux": address = db_ssource.offset if address < 0: address += (1 << 64) else: if module not in offset_map: self.log_debug( "Module '{0}' not found in package '{1}'".format( module, task.debuginfo.nvra)) db_ssource.retrace_fail_count += 1 continue module_map = offset_map[module] symbol_name = db_ssource.symbol.name if symbol_name not in module_map: symbol_name = symbol_name.lstrip("_") if symbol_name not in module_map: self.log_debug("Function '{0}' not found in module " "'{1}'".format(db_ssource.symbol.name, module)) db_ssource.retrace_fail_count += 1 continue address = module_map[symbol_name] + db_ssource.func_offset debug_dir = os.path.join(task.debuginfo.unpacked_path, "usr", "lib", "debug") debug_path = self._get_debug_path(db, module, task.debuginfo.db_package) if debug_path is None: db_ssource.retrace_fail_count += 1 continue try: abspath = os.path.join(task.debuginfo.unpacked_path, debug_path[1:]) results = addr2line(abspath, address, debug_dir) results.reverse() except FafError as ex: 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, module) if db_symbol_inl is None: sym_key = (funcname, module) 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 = module 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 = module 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, module) if db_symbol is None: key = (funcname, module) if key in new_symbols: db_symbol = new_symbols[key] else: self.log_debug( "Creating new symbol '{0}' @ '{1}'".format( funcname, module)) db_symbol = Symbol() db_symbol.name = funcname db_symbol.normalized_path = module 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 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)
def save_ureport(self, db, db_report, ureport, flush=False, count=1): bthash1 = self._hash_koops(ureport["frames"], skip_unreliable=False) bthash2 = self._hash_koops(ureport["frames"], skip_unreliable=True) if not db_report.backtraces: db_backtrace = ReportBacktrace() db_backtrace.report = db_report db.session.add(db_backtrace) db_thread = ReportBtThread() db_thread.backtrace = db_backtrace db_thread.crashthread = True db.session.add(db_thread) db_bthash1 = ReportBtHash() db_bthash1.backtrace = db_backtrace db_bthash1.hash = bthash1 db_bthash1.type = "NAMES" db.session.add(db_bthash1) if bthash2 is not None and bthash1 != bthash2: db_bthash2 = ReportBtHash() db_bthash2.backtrace = db_backtrace db_bthash2.hash = bthash2 db_bthash2.type = "NAMES" db.session.add(db_bthash2) new_symbols = {} new_symbolsources = {} i = 0 for frame in ureport["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. i += 10 # nah, another hack, deals with wrong parsing if frame["function_name"].startswith("0x"): continue if not "module_name" in frame: module = "vmlinux" else: module = frame["module_name"] db_symbol = get_symbol_by_name_path(db, frame["function_name"], module) if db_symbol is None: key = (frame["function_name"], module) if key in new_symbols: db_symbol = new_symbols[key] else: db_symbol = Symbol() db_symbol.name = frame["function_name"] db_symbol.normalized_path = module db.session.add(db_symbol) new_symbols[key] = db_symbol # this doesn't work well. on 64bit, kernel maps to # the end of address space (64bit unsigned), but in # postgres bigint is 64bit signed and can't save # the value - let's just map it to signed if "address" in frame: if frame["address"] >= (1 << 63): address = frame["address"] - (1 << 64) else: address = frame["address"] else: address = 0 db_symbolsource = get_ssource_by_bpo(db, ureport["version"], module, address) if db_symbolsource is None: key = (ureport["version"], module, address) if key in new_symbolsources: db_symbolsource = new_symbolsources[key] else: db_symbolsource = SymbolSource() db_symbolsource.path = module db_symbolsource.offset = address db_symbolsource.func_offset = frame["function_offset"] db_symbolsource.symbol = db_symbol db_symbolsource.build_id = ureport["version"] db.session.add(db_symbolsource) new_symbolsources[key] = db_symbolsource db_frame = ReportBtFrame() db_frame.thread = db_thread db_frame.order = i db_frame.symbolsource = db_symbolsource db_frame.inlined = False db_frame.reliable = frame["reliable"] db.session.add(db_frame) for taintflag in ureport["taint_flags"]: db_taintflag = get_taint_flag_by_ureport_name(db, taintflag) if db_taintflag is None: self.log_warn( "Skipping unsupported taint flag '{0}'".format( taintflag)) continue db_bttaintflag = ReportBtTaintFlag() db_bttaintflag.backtrace = db_backtrace db_bttaintflag.taintflag = db_taintflag db.session.add(db_bttaintflag) if "modules" in ureport: new_modules = {} # use set() to remove duplicates for module in set(ureport["modules"]): idx = module.find("(") if idx >= 0: module = module[:idx] db_module = get_kernelmodule_by_name(db, module) if db_module is None: if module in new_modules: db_module = new_modules[module] else: db_module = KernelModule() db_module.name = module db.session.add(db_module) new_modules[module] = db_module db_btmodule = ReportBtKernelModule() db_btmodule.kernelmodule = db_module db_btmodule.backtrace = db_backtrace db.session.add(db_btmodule) # do not overwrite an existing oops if not db_report.has_lob("oops"): # do not append here, but create a new dict # we only want save_ureport_post_flush process the most # recently saved report self.add_lob = {db_report: ureport["raw_oops"].encode("utf-8")} if flush: db.session.flush()