Exemplo n.º 1
0
    def symbols(self):
        self.begin('Symbols')
        for fun, lib in itertools.product(data.FUNS, data.LIBS):
            symbolsource = SymbolSource()
            symbolsource.build_id = random.randrange(1, 100)
            symbolsource.line_number = random.randrange(1, 100)
            symbolsource.source_path = '/usr/lib64/python2.7/%s.py' % lib
            symbolsource.path = '/usr/lib64/python2.7/%s.pyo' % lib
            symbolsource.hash = randutils.randhash()
            symbolsource.offset = randutils.randhash()

            symbol = Symbol()
            symbol.name = fun
            symbol.normalized_path = lib
            self.add(symbol)

            symbolsource.symbol = symbol
            self.add(symbolsource)

        self.commit()
Exemplo n.º 2
0
    def symbols(self):
        self.begin('Symbols')
        for fun, lib in itertools.product(data.FUNS, data.LIBS):
            symbolsource = SymbolSource()
            symbolsource.build_id = random.randrange(1, 100)
            symbolsource.line_number = random.randrange(1, 100)
            symbolsource.source_path = '/usr/lib64/python2.7/%s.py' % lib
            symbolsource.path = '/usr/lib64/python2.7/%s.pyo' % lib
            symbolsource.hash = randutils.randhash()
            symbolsource.offset = randutils.randhash()

            symbol = Symbol()
            symbol.name = fun
            symbol.normalized_path = lib
            self.add(symbol)

            symbolsource.symbol = symbol
            self.add(symbolsource)

        self.commit()
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
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()
Exemplo n.º 5
0
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