示例#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()