예제 #1
0
파일: retrace.py 프로젝트: rtnpro/faf
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()
예제 #2
0
파일: retrace.py 프로젝트: rtnpro/faf
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)