Example #1
0
SymbolMap::const_iterator findCursorInfo(const SymbolMap &map, const Location &location, const String &context,
                                         const SymbolMap *errors, bool *foundInErrors)
{
    if (foundInErrors)
        *foundInErrors = false;
    if (map.isEmpty() && !errors)
        return map.end();
    if (errors) {
        SymbolMap::const_iterator ret = findCursorInfo(map, location, context, false);
        if (ret != map.end()) {
            return ret;
        }
        ret = findCursorInfo(*errors, location, context, false);
        if (ret != errors->end()) {
            if (foundInErrors)
                *foundInErrors = true;
            return ret;
        }
        // ret = findCursorInfo(*errors, location, context, true);
        // if (ret != errors->end()) {
        //     if (foundInErrors)
        //         *foundInErrors = true;
        //     return ret;
        // }
        // ret = findCursorInfo(map, location, context, true);
        // if (ret != map.end()) {
        //     return ret;
        // }

        return map.end();
    } else {
        const SymbolMap::const_iterator ret = findCursorInfo(map, location, context, true);
        return ret;
    }
}
Example #2
0
static inline void allImpl(const SymbolMap &map, const Location &loc, const CursorInfo &info, SymbolMap &out, Mode mode, CXCursorKind kind)
{
    if (out.contains(loc))
        return;
    out[loc] = info;
    typedef SymbolMap (CursorInfo::*Function)(const SymbolMap &map) const;
    const SymbolMap targets = info.targetInfos(map);
    for (SymbolMap::const_iterator t = targets.begin(); t != targets.end(); ++t) {
        bool ok = false;
        switch (mode) {
        case VirtualRefs:
        case NormalRefs:
            ok = (t->second.kind == kind);
            break;
        case ClassRefs:
            ok = (t->second.isClass()
                  || t->second.kind == CXCursor_Destructor
                  || t->second.kind == CXCursor_Constructor);
            break;
        }
        if (ok)
            allImpl(map, t->first, t->second, out, mode, kind);
    }
    const SymbolMap refs = info.referenceInfos(map);
    for (SymbolMap::const_iterator r = refs.begin(); r != refs.end(); ++r) {
        switch (mode) {
        case NormalRefs:
            out[r->first] = r->second;
            break;
        case VirtualRefs:
            if (r->second.kind == kind) {
                allImpl(map, r->first, r->second, out, mode, kind);
            } else {
                out[r->first] = r->second;
            }
            break;
        case ClassRefs:
            if (info.isClass()) // for class/struct we want the references inserted directly regardless and also recursed
                out[r->first] = r->second;
            if (r->second.isClass()
                || r->second.kind == CXCursor_Destructor
                || r->second.kind == CXCursor_Constructor) { // if is a constructor/destructor/class reference we want to recurse it
                allImpl(map, r->first, r->second, out, mode, kind);
            }
        }
    }
}
	std::string getSymbol( const std::string &varName ) {
		SymbolMap::iterator sbmItr = _symbolMap.find( varName );
		if ( sbmItr == _symbolMap.end() ) {
			SpecialSymbolMap::iterator ssmItr = _specialSymbolMap.find( varName );
			return ssmItr == _specialSymbolMap.end() ? varName : ssmItr->second;
		}
		return sbmItr->second.back();
	}
Example #4
0
SymbolMap CursorInfo::callers(const Location &loc, const SymbolMap &map, const SymbolMap *errors) const
{
    SymbolMap ret;
    const SymbolMap cursors = virtuals(loc, map, errors);
    for (SymbolMap::const_iterator c = cursors.begin(); c != cursors.end(); ++c) {
        for (Set<Location>::const_iterator it = c->second.references.begin(); it != c->second.references.end(); ++it) {
            const SymbolMap::const_iterator found = RTags::findCursorInfo(map, *it, String(), errors);
            if (found == map.end())
                continue;
            if (RTags::isReference(found->second.kind)) { // is this always right?
                ret[*it] = found->second;
            } else if (kind == CXCursor_Constructor && (found->second.kind == CXCursor_VarDecl || found->second.kind == CXCursor_FieldDecl)) {
                ret[*it] = found->second;
            }
        }
    }
    return ret;
}
Example #5
0
static inline void allImpl(const SymbolMap &map, const Location &loc, const std::shared_ptr<CursorInfo> &info, SymbolMap &out, Mode mode, unsigned kind)
{
    if (out.contains(loc))
        return;
    out[loc] = info;
    const SymbolMap targets = info->targetInfos(map);
    for (auto t = targets.begin(); t != targets.end(); ++t) {
        bool ok = false;
        switch (mode) {
        case VirtualRefs:
        case NormalRefs:
            ok = (t->second->kind == kind);
            break;
        case ClassRefs:
            ok = (t->second->isClass() || t->second->kind == CXCursor_Destructor || t->second->kind == CXCursor_Constructor);
            break;
        }
        if (ok)
            allImpl(map, t->first, t->second, out, mode, kind);
    }
    const SymbolMap refs = info->referenceInfos(map);
    for (auto r = refs.begin(); r != refs.end(); ++r) {
        switch (mode) {
        case NormalRefs:
            out[r->first] = r->second;
            break;
        case VirtualRefs:
            if (r->second->kind == kind) {
                allImpl(map, r->first, r->second, out, mode, kind);
            } else {
                out[r->first] = r->second;
            }
            break;
        case ClassRefs:
            if (info->isClass()) // for class/struct we want the references inserted directly regardless and also recursed
                out[r->first] = r->second;
            if (r->second->isClass()
                || r->second->kind == CXCursor_Destructor
                || r->second->kind == CXCursor_Constructor) { // if is a constructor/destructor/class reference we want to recurse it
                allImpl(map, r->first, r->second, out, mode, kind);
            }
        }
    }
}
Example #6
0
SymbolMap CursorInfo::referenceInfos(const SymbolMap &map) const
{
    SymbolMap ret;
    for (auto it = references.begin(); it != references.end(); ++it) {
        auto found = RTags::findCursorInfo(map, *it, String());
        if (found != map.end()) {
            ret[*it] = found->second;
        }
    }
    return ret;
}
Example #7
0
SymbolMap CursorInfo::virtuals(const Location &loc, const SymbolMap &map) const
{
    SymbolMap ret;
    ret[loc] = copy();
    const SymbolMap s = (kind == CXCursor_CXXMethod ? allReferences(loc, map) : targetInfos(map));
    for (auto it = s.begin(); it != s.end(); ++it) {
        if (it->second->kind == kind)
            ret[it->first] = it->second;
    }
    return ret;
}
Example #8
0
static inline void writeCursors(SymbolMap &symbols, SymbolMap &current)
{
    if (!symbols.isEmpty()) {
        if (current.isEmpty()) {
            current = symbols;
        } else {
            SymbolMap::iterator it = symbols.begin();
            const SymbolMap::iterator end = symbols.end();
            while (it != end) {
                SymbolMap::iterator cur = current.find(it->first);
                if (cur == current.end()) {
                    current[it->first] = it->second;
                } else {
                    cur->second.unite(it->second);
                }
                ++it;
            }
        }
    }
}
Example #9
0
SymbolMap CursorInfo::referenceInfos(const SymbolMap &map, const SymbolMap *errors) const
{
    SymbolMap ret;
    for (Set<Location>::const_iterator it = references.begin(); it != references.end(); ++it) {
        SymbolMap::const_iterator found = RTags::findCursorInfo(map, *it, String(), errors);
        if (found != map.end()) {
            ret[*it] = found->second;
        }
    }
    return ret;
}
Example #10
0
SymbolMap CursorInfo::virtuals(const Location &loc, const SymbolMap &map, const SymbolMap *errors) const
{
    SymbolMap ret;
    ret[loc] = *this;
    const SymbolMap s = (kind == CXCursor_CXXMethod ? allReferences(loc, map, errors) : targetInfos(map, errors));
    for (SymbolMap::const_iterator it = s.begin(); it != s.end(); ++it) {
        if (it->second.kind == kind)
            ret[it->first] = it->second;
    }
    return ret;
}
Example #11
0
void dirtySymbols(SymbolMap &map, const Set<uint32_t> &dirty)
{
    SymbolMap::iterator it = map.begin();
    while (it != map.end()) {
        if (dirty.contains(it->first.fileId())) {
            map.erase(it++);
        } else {
            it->second->dirty(dirty);
            ++it;
        }
    }
}
Example #12
0
CursorInfo CursorInfo::bestTarget(const SymbolMap &map, const SymbolMap *errors, Location *loc) const
{
    const SymbolMap targets = targetInfos(map, errors);

    SymbolMap::const_iterator best = targets.end();
    int bestRank = -1;
    for (SymbolMap::const_iterator it = targets.begin(); it != targets.end(); ++it) {
        const CursorInfo &ci = it->second;
        const int r = targetRank(ci);
        if (r > bestRank || (r == bestRank && ci.isDefinition())) {
            bestRank = r;
            best = it;
        }
    }
    if (best != targets.end()) {
        if (loc)
            *loc = best->first;
        return best->second;
    }
    return CursorInfo();
}
Example #13
0
std::shared_ptr<CursorInfo> CursorInfo::bestTarget(const SymbolMap &map, Location *loc) const
{
    const SymbolMap targets = targetInfos(map);

    auto best = targets.end();
    int bestRank = -1;
    for (auto it = targets.begin(); it != targets.end(); ++it) {
        const std::shared_ptr<CursorInfo> &ci = it->second;
        const int r = targetRank(ci);
        if (r > bestRank || (r == bestRank && ci->isDefinition())) {
            bestRank = r;
            best = it;
        }
    }
    if (best != targets.end()) {
        if (loc)
            *loc = best->first;
        return best->second;
    }
    return std::shared_ptr<CursorInfo>();
}
Example #14
0
static inline void joinCursors(SymbolMap &symbols, const Set<Location> &locations)
{
    for (Set<Location>::const_iterator it = locations.begin(); it != locations.end(); ++it) {
        SymbolMap::iterator c = symbols.find(*it);
        if (c != symbols.end()) {
            CursorInfo &cursorInfo = c->second;
            for (Set<Location>::const_iterator innerIt = locations.begin(); innerIt != locations.end(); ++innerIt) {
                if (innerIt != it)
                    cursorInfo.targets.insert(*innerIt);
            }
            // ### this is filthy, we could likely think of something better
        }
    }
}
Example #15
0
static inline SymbolMap::const_iterator findCursorInfo(const SymbolMap &map, const Location &location, const String &context, bool scan)
{
    if (context.isEmpty() || !scan) {
        SymbolMap::const_iterator it = map.lower_bound(location);
        if (it != map.end() && it->first == location) {
            return it;
        } else if (it != map.begin()) {
            --it;
            if (it->first.fileId() == location.fileId()) {
                const int off = location.offset() - it->first.offset();
                if (it->second.symbolLength > off && (context.isEmpty() || it->second.symbolName.contains(context))) {
                    return it;
                }
            }
        }
        return map.end();
    }

    SymbolMap::const_iterator f = map.lower_bound(location);
    if (f != map.begin() && (f == map.end() || f->first != location))
        --f;
    SymbolMap::const_iterator b = f;

    enum { Search = 32 };
    for (int j=0; j<Search; ++j) {
        if (f != map.end()) {
            if (location.fileId() != f->first.fileId()) {
                if (b == map.begin())
                    break;
                f = map.end();
            } else if (f->second.symbolName.contains(context)) {
                // error() << "found it forward" << j;
                return f;
            } else {
                ++f;
            }
        }

        if (b != map.begin()) {
            --b;
            if (location.fileId() != b->first.fileId()) {
                if (f == map.end())
                    break;
                b = map.begin();
            } else if (b->second.symbolName.contains(context)) {
                // error() << "found it backward" << j;
                return b;
            }
        }
    }
    return map.end();
}
Example #16
0
SymbolMap CursorInfo::targetInfos(const SymbolMap &map) const
{
    SymbolMap ret;
    for (Set<Location>::const_iterator it = targets.begin(); it != targets.end(); ++it) {
        SymbolMap::const_iterator found = RTags::findCursorInfo(map, *it);
        if (found != map.end()) {
            ret[*it] = found->second;
        } else {
            ret[*it] = CursorInfo();
            // we need this one for inclusion directives which target a
            // non-existing CursorInfo
        }
    }
    return ret;
}
SymbolMap::const_iterator findCursorInfo(const SymbolMap &map, const Location &location)
{
    if (map.isEmpty())
        return map.end();

    SymbolMap::const_iterator it = map.find(location);
    if (it != map.end())
        return it;
    it = map.lower_bound(location);
    if (it == map.end()) {
        --it;
    } else {
        const int cmp = it->first.compare(location);
        if (!cmp)
            return it;
        --it;
    }
    if (location.fileId() != it->first.fileId())
        return map.end();
    const int off = location.offset() - it->first.offset();
    if (it->second.symbolLength > off)
        return it;
    return map.end();
}
Example #18
0
SymbolMap CursorInfo::targetInfos(const SymbolMap &map, const SymbolMap *errors) const
{
    SymbolMap ret;
    for (Set<Location>::const_iterator it = targets.begin(); it != targets.end(); ++it) {
        SymbolMap::const_iterator found = RTags::findCursorInfo(map, *it, String(), errors);
        // ### could/should I pass symbolName as context here?
        if (found != map.end()) {
            ret[*it] = found->second;
        } else {
            ret[*it] = CursorInfo();
            // we need this one for inclusion directives which target a
            // non-existing CursorInfo
        }
    }
    return ret;
}
Example #19
0
SymbolMap CursorInfo::targetInfos(const SymbolMap &map) const
{
    SymbolMap ret;
    for (auto it = targets.begin(); it != targets.end(); ++it) {
        auto found = RTags::findCursorInfo(map, *it, String());
        // ### could/should I pass symbolName as context here?
        if (found != map.end()) {
            ret[*it] = found->second;
        } else {
            ret[*it] = std::make_shared<CursorInfo>();
            // we need this one for inclusion directives which target a
            // non-existing CursorInfo
        }
    }
    return ret;
}
Example #20
0
static inline void writeErrorSymbols(const SymbolMap &symbols, ErrorSymbolMap &errorSymbols, const Map<uint32_t, int> &errors)
{
    for (Map<uint32_t, int>::const_iterator it = errors.begin(); it != errors.end(); ++it) {
        if (it->second) {
            SymbolMap &symbolsForFile = errorSymbols[it->first];
            if (symbolsForFile.isEmpty()) {
                const Location loc(it->first, 0);
                SymbolMap::const_iterator sit = symbols.lower_bound(loc);
                while (sit != symbols.end() && sit->first.fileId() == it->first) {
                    symbolsForFile[sit->first] = sit->second;
                    ++sit;
                }
            }
        } else {
            errorSymbols.remove(it->first);
        }
    }
}
Example #21
0
static void FindSymbols(butil::IOBuf* out, std::vector<uintptr_t>& addr_list) {
    char buf[32];
    for (size_t i = 0; i < addr_list.size(); ++i) {
        int len = snprintf(buf, sizeof(buf), "0x%08lx\t", addr_list[i]);
        out->append(buf, len);
        SymbolMap::const_iterator it = symbol_map.lower_bound(addr_list[i]);
        if (it == symbol_map.end() || it->first != addr_list[i]) {
            if (it != symbol_map.begin()) {
                --it;
            } else {
                len = snprintf(buf, sizeof(buf), "0x%08lx\n", addr_list[i]);
                out->append(buf, len);
                continue;
            }
        }
        if (it->second.empty()) {
            len = snprintf(buf, sizeof(buf), "0x%08lx\n", addr_list[i]);
            out->append(buf, len);
        } else {
            out->append(it->second);
            out->push_back('\n');
        }
    }
}
void ReferencesJob::execute()
{
    shared_ptr<Project> proj = project();
    Location startLocation;
    Set<Location> references;
    if (proj) {
        if (!symbolName.isEmpty()) {
            Scope<const SymbolNameMap&> scope = proj->lockSymbolNamesForRead();
            if (scope.isNull())
                return;
            locations = scope.data().value(symbolName);
        }
        if (!locations.isEmpty()) {
            Scope<const SymbolMap&> scope = proj->lockSymbolsForRead();
            if (scope.isNull())
                return;

            const SymbolMap &map = scope.data();
            for (Set<Location>::const_iterator it = locations.begin(); it != locations.end(); ++it) {
                Location pos;
                CursorInfo cursorInfo = RTags::findCursorInfo(map, *it, &pos);
                if (startLocation.isNull())
                    startLocation = pos;
                if (RTags::isReference(cursorInfo.kind)) {
                    cursorInfo = cursorInfo.bestTarget(map, &pos);
                }
                if (queryFlags() & QueryMessage::ReferencesForRenameSymbol) {
                    const SymbolMap all = cursorInfo.allReferences(pos, map);
                    bool classRename = false;
                    switch (cursorInfo.kind) {
                    case CXCursor_Constructor:
                    case CXCursor_Destructor:
                        classRename = true;
                        break;
                    default:
                        classRename = cursorInfo.isClass();
                        break;
                    }

                    for (SymbolMap::const_iterator a = all.begin(); a != all.end(); ++a) {
                        if (!classRename) {
                            references.insert(a->first);
                        } else {
                            enum State {
                                FoundConstructor = 0x1,
                                FoundClass = 0x2,
                                FoundReferences = 0x4
                            };
                            unsigned state = 0;
                            const SymbolMap targets = a->second.targetInfos(map);
                            for (SymbolMap::const_iterator t = targets.begin(); t != targets.end(); ++t) {
                                if (t->second.kind != a->second.kind)
                                    state |= FoundReferences;
                                if (t->second.kind == CXCursor_Constructor) {
                                    state |= FoundConstructor;
                                } else if (t->second.isClass()) {
                                    state |= FoundClass;
                                }
                            }
                            if ((state & (FoundConstructor|FoundClass)) != FoundConstructor || !(state & FoundReferences)) {
                                references.insert(a->first);
                            }
                        }
                    }
                } else if (queryFlags() & QueryMessage::FindVirtuals) {
                    const SymbolMap virtuals = cursorInfo.virtuals(pos, map);
                    for (SymbolMap::const_iterator v = virtuals.begin(); v != virtuals.end(); ++v) {
                        references.insert(v->first);
                    }
                } else {
                    const SymbolMap callers = cursorInfo.callers(pos, map);
                    for (SymbolMap::const_iterator c = callers.begin(); c != callers.end(); ++c) {
                        references.insert(c->first);
                    }
                }
            }
        }
    }

    List<Location> sorted = references.toList();
    if (queryFlags() & QueryMessage::ReverseSort) {
        std::sort(sorted.begin(), sorted.end(), std::greater<Location>());
    } else {
        std::sort(sorted.begin(), sorted.end());
    }
    // We don't want to do the startIndex stuff when renaming. The only way to
    // tell the difference between rtags-find-all-references and
    // rtags-rename-symbol is that the latter does a reverse sort. It kinda
    // doesn't make sense to have this behavior in reverse sort anyway so I
    // won't formalize the rename parameters to indicate that we're renaming
    int startIndex = 0;
    const int count = sorted.size();
    if (!(queryFlags() & QueryMessage::ReverseSort) && sorted.size() != 1 && !startLocation.isNull()) {
        startIndex = sorted.indexOf(startLocation) + 1;
    }
    const unsigned keyFlags = Job::keyFlags();

    for (int i=0; i<count; ++i) {
        const Location &loc = sorted.at((startIndex + i) % count);
        write(loc.key(keyFlags));
    }
}
Example #23
0
int ReferencesJob::execute()
{
    std::shared_ptr<Project> proj = project();
    Location startLocation;
    Map<Location, std::pair<bool, uint16_t> > references;
    if (proj) {
        if (!symbolName.isEmpty())
            locations = proj->locations(symbolName);
        if (!locations.isEmpty()) {
            const SymbolMap &map = proj->symbols();

            for (Set<Location>::const_iterator it = locations.begin(); it != locations.end(); ++it) {
                Location pos;
                SymbolMap::const_iterator found;
                found = RTags::findCursorInfo(map, *it);
                if (found == map.end())
                    continue;
                pos = found->first;
                if (startLocation.isNull())
                    startLocation = pos;
                std::shared_ptr<CursorInfo> cursorInfo = found->second;
                if (!cursorInfo)
                    continue;
                if (RTags::isReference(cursorInfo->kind)) {
                    cursorInfo = cursorInfo->bestTarget(map, &pos);
                    if (!cursorInfo)
                        continue;
                }
                if (queryFlags() & QueryMessage::AllReferences) {
                    const SymbolMap all = cursorInfo->allReferences(pos, map);

                    bool classRename = false;
                    switch (cursorInfo->kind) {
                    case CXCursor_Constructor:
                    case CXCursor_Destructor:
                        classRename = true;
                        break;
                    default:
                        classRename = cursorInfo->isClass();
                        break;
                    }

                    for (SymbolMap::const_iterator a = all.begin(); a != all.end(); ++a) {
                        if (!classRename) {
                            references[a->first] = std::make_pair(a->second->isDefinition(), a->second->kind);
                        } else {
                            enum State {
                                FoundConstructor = 0x1,
                                FoundClass = 0x2,
                                FoundReferences = 0x4
                            };
                            unsigned state = 0;
                            const SymbolMap targets = a->second->targetInfos(map);
                            for (SymbolMap::const_iterator t = targets.begin(); t != targets.end(); ++t) {
                                if (t->second->kind != a->second->kind)
                                    state |= FoundReferences;
                                if (t->second->kind == CXCursor_Constructor) {
                                    state |= FoundConstructor;
                                } else if (t->second->isClass()) {
                                    state |= FoundClass;
                                }
                            }
                            if ((state & (FoundConstructor|FoundClass)) != FoundConstructor || !(state & FoundReferences)) {
                                references[a->first] = std::make_pair(a->second->isDefinition(), a->second->kind);
                            }
                        }
                    }
                } else if (queryFlags() & QueryMessage::FindVirtuals) {
                    const SymbolMap virtuals = cursorInfo->virtuals(pos, map);
                    const bool declarationOnly = queryFlags() & QueryMessage::DeclarationOnly;
                    for (SymbolMap::const_iterator v = virtuals.begin(); v != virtuals.end(); ++v) {
                        const bool def = v->second->isDefinition();
                        if (declarationOnly && def) {
                            const std::shared_ptr<CursorInfo> decl = v->second->bestTarget(map);
                            if (decl && !decl->isNull())
                                continue;
                        }
                        references[v->first] = std::make_pair(def, v->second->kind);
                    }
                    startLocation.clear();
                    // since one normally calls this on a declaration it kinda
                    // doesn't work that well do the clever offset thing
                    // underneath
                } else {
                    const SymbolMap callers = cursorInfo->callers(pos, map);
                    for (SymbolMap::const_iterator c = callers.begin(); c != callers.end(); ++c) {
                        references[c->first] = std::make_pair(false, CXCursor_FirstInvalid);
                        // For find callers we don't want to prefer definitions or do ranks on cursors
                    }
                }
            }
        }
    }
    enum { Rename = (QueryMessage::ReverseSort|QueryMessage::AllReferences) };
    if ((queryFlags() & Rename) == Rename) {
        if (!references.isEmpty()) {
            Map<Location, std::pair<bool, uint16_t> >::const_iterator it = references.end();
            do {
                --it;
                write(it->first);
            } while (it != references.begin());
            return 0;
        }
    } else {
        List<RTags::SortedCursor> sorted;
        sorted.reserve(references.size());
        for (Map<Location, std::pair<bool, uint16_t> >::const_iterator it = references.begin();
             it != references.end(); ++it) {
            sorted.append(RTags::SortedCursor(it->first, it->second.first, it->second.second));
        }
        if (queryFlags() & QueryMessage::ReverseSort) {
            std::sort(sorted.begin(), sorted.end(), std::greater<RTags::SortedCursor>());
        } else {
            std::sort(sorted.begin(), sorted.end());
        }
        int startIndex = 0;
        const int count = sorted.size();
        if (!startLocation.isNull() && !(queryFlags() & QueryMessage::NoSortReferencesByInput)) {
            for (int i=0; i<count; ++i) {
                if (sorted.at(i).location == startLocation) {
                    startIndex = i + 1;
                    break;
                }
            }
        }

        for (int i=0; i<count; ++i) {
            const Location &loc = sorted.at((startIndex + i) % count).location;
            write(loc);
        }
        if (count)
            return 0;
    }
    return 1;
}
Example #24
0
static void LoadSymbols() {
    butil::Timer tm;
    tm.start();
    butil::ScopedFILE fp(fopen("/proc/self/maps", "r"));
    if (fp == NULL) {
        return;
    }
    char* line = NULL;
    size_t line_len = 0;
    ssize_t nr = 0;
    while ((nr = getline(&line, &line_len, fp.get())) != -1) {
        butil::StringSplitter sp(line, line + line_len, ' ');
        if (sp == NULL) {
            continue;
        }
        char* endptr;
        uintptr_t start_addr = strtoull(sp.field(), &endptr, 16);
        if (*endptr != '-') {
            continue;
        }
        ++endptr;
        uintptr_t end_addr = strtoull(endptr, &endptr, 16);
        if (*endptr != ' ') {
            continue;
        }
        ++sp;
        // ..x. must be executable
        if (sp == NULL || sp.length() != 4 || sp.field()[2] != 'x') {
            continue;
        }
        ++sp;
        if (sp == NULL) {
            continue;
        }
        size_t offset = strtoull(sp.field(), &endptr, 16);
        if (*endptr != ' ') {
            continue;
        }        
        //skip $4~$5
        for (int i = 0; i < 3; ++i) {
            ++sp;
        }
        if (sp == NULL) {
            continue;
        }
        size_t n = sp.length();
        if (sp.field()[n-1] == '\n') {
            --n;
        }
        std::string path(sp.field(), n);
        if (!HasExt(path, ".so") && !HasExt(path, ".dll") &&
            !HasExt(path, ".dylib") && !HasExt(path, ".bundle")) {
            continue;
        }
        LibInfo info;
        info.start_addr = start_addr;
        info.end_addr = end_addr;
        info.offset = offset;
        info.path = path;
        ExtractSymbolsFromBinary(symbol_map, info);
    }
    free(line);

    LibInfo info;
    info.start_addr = 0;
    info.end_addr = std::numeric_limits<uintptr_t>::max();
    info.offset = 0;
    info.path = program_invocation_name;
    ExtractSymbolsFromBinary(symbol_map, info);

    butil::Timer tm2;
    tm2.start();
    size_t num_removed = 0;
    bool last_is_empty = false;
    for (SymbolMap::iterator
             it = symbol_map.begin(); it != symbol_map.end();) {
        if (it->second.empty()) {
            if (last_is_empty) {
                symbol_map.erase(it++);
                ++num_removed;
            } else {
                ++it;
            }
            last_is_empty = true;
        } else {
            ++it;
        }
    }
    tm2.stop();
    RPC_VLOG_IF(num_removed) << "Removed " << num_removed << " entries in "
        << tm2.m_elapsed() << "ms";

    tm.stop();
    RPC_VLOG << "Loaded all symbols in " << tm.m_elapsed() << "ms";
}
	bool atTopLevel( const std::string &varName ) {
		SymbolMap::iterator sbmItr = _symbolMap.find( varName );
		if ( sbmItr == _symbolMap.end() ) return true;
		return sbmItr->second.size() <= 1;
	}