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(); }
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); } } } }
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); } } } }
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; }
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; }
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; } } }
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; }
static inline void writeCursors(SymbolMap &symbols, SymbolMap ¤t) { 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; } } } }
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(); }
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>(); }
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'); } } }
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"; }
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; }
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)); } }