| //===--- JSONAggregation.cpp - Index data aggregation in JSON format ------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "JSONAggregation.h" |
| #include "indexstore/IndexStoreCXX.h" |
| #include "clang/Index/IndexDataStoreSymbolUtils.h" |
| #include "llvm/ADT/StringMap.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace clang; |
| using namespace clang::index; |
| using namespace indexstore; |
| using namespace llvm; |
| |
| #if INDEXSTORE_HAS_BLOCKS |
| |
| namespace { |
| |
| typedef size_t FilePathIndex; |
| typedef size_t RecordIndex; |
| typedef size_t SymbolIndex; |
| |
| struct UnitSourceInfo { |
| FilePathIndex FilePath; |
| SmallVector<RecordIndex, 2> AssociatedRecords; |
| }; |
| |
| struct UnitInfo { |
| std::string Name; |
| SmallVector<UnitSourceInfo, 8> Sources; |
| SmallVector<std::string, 3> UnitDepends; |
| FilePathIndex OutFile; |
| StringRef Triple; |
| }; |
| |
| struct SymbolInfo { |
| SymbolKind Kind; |
| SymbolLanguage Lang; |
| StringRef USR; |
| StringRef Name; |
| StringRef CodegenName; |
| SymbolRoleSet Roles = 0; |
| SymbolRoleSet RelatedRoles = 0; |
| }; |
| |
| struct SymbolRelationInfo { |
| SymbolIndex RelatedSymbol; |
| SymbolRoleSet Roles; |
| SymbolRelationInfo(SymbolIndex relSymbol, SymbolRoleSet roles) |
| : RelatedSymbol(relSymbol), Roles(roles) {} |
| }; |
| |
| struct SymbolOccurrenceInfo { |
| SymbolIndex Symbol; |
| SymbolRoleSet Roles = 0; |
| std::vector<SymbolRelationInfo> Relations; |
| unsigned Line; |
| unsigned Column; |
| }; |
| |
| struct RecordInfo { |
| SmallVector<SymbolOccurrenceInfo, 8> Occurrences; |
| }; |
| |
| class Aggregator { |
| IndexStore Store; |
| |
| BumpPtrAllocator Allocator; |
| |
| StringMap<FilePathIndex, BumpPtrAllocator &> FilePathIndices; |
| std::vector<StringRef> FilePaths; |
| StringMap<char, BumpPtrAllocator &> Triples; |
| |
| std::vector<std::unique_ptr<UnitInfo>> Units; |
| |
| StringMap<RecordIndex, BumpPtrAllocator &> RecordIndices; |
| std::vector<std::unique_ptr<RecordInfo>> Records; |
| |
| StringMap<SymbolIndex, BumpPtrAllocator &> SymbolIndices; |
| std::vector<SymbolInfo> Symbols; |
| |
| public: |
| explicit Aggregator(IndexStore store) |
| : Store(std::move(store)), |
| FilePathIndices(Allocator), |
| Triples(Allocator), |
| RecordIndices(Allocator), |
| SymbolIndices(Allocator) {} |
| |
| bool process(); |
| void processUnit(StringRef name, IndexUnitReader &UnitReader); |
| void dumpJSON(raw_ostream &OS); |
| |
| private: |
| StringRef copyStr(StringRef str) { |
| if (str.empty()) |
| return StringRef(); |
| char *buf = Allocator.Allocate<char>(str.size()); |
| std::copy(str.begin(), str.end(), buf); |
| return StringRef(buf, str.size()); |
| } |
| |
| StringRef getTripleString(StringRef inputTriple) { |
| return Triples.insert(std::make_pair(inputTriple, 0)).first->first(); |
| } |
| |
| FilePathIndex getFilePathIndex(StringRef path, StringRef workingDir); |
| RecordIndex getRecordIndex(StringRef recordFile); |
| SymbolIndex getSymbolIndex(IndexRecordSymbol sym); |
| std::unique_ptr<RecordInfo> processRecord(StringRef recordFile); |
| }; |
| |
| } // anonymous namespace |
| |
| bool Aggregator::process() { |
| bool succ = Store.foreachUnit(/*sorted=*/true, [&](StringRef unitName) -> bool { |
| std::string error; |
| auto unitReader = IndexUnitReader(Store, unitName, error); |
| if (!unitReader) { |
| errs() << "error opening unit file '" << unitName << "': " << error << '\n'; |
| return false; |
| } |
| |
| processUnit(unitName, unitReader); |
| return true; |
| }); |
| |
| return !succ; |
| } |
| |
| void Aggregator::processUnit(StringRef name, IndexUnitReader &UnitReader) { |
| auto workDir = UnitReader.getWorkingDirectory(); |
| auto unit = llvm::make_unique<UnitInfo>(); |
| unit->Name = name; |
| unit->Triple = getTripleString(UnitReader.getTarget()); |
| unit->OutFile = getFilePathIndex(UnitReader.getOutputFile(), workDir); |
| |
| struct DepInfo { |
| UnitSourceInfo source; |
| std::string unitName; |
| }; |
| SmallVector<DepInfo, 32> Deps; |
| UnitReader.foreachDependency([&](IndexUnitDependency dep) -> bool { |
| Deps.resize(Deps.size()+1); |
| auto &depInfo = Deps.back(); |
| switch (dep.getKind()) { |
| case IndexUnitDependency::DependencyKind::Unit: { |
| depInfo.unitName = dep.getName(); |
| StringRef filePath = dep.getFilePath(); |
| if (!filePath.empty()) |
| depInfo.source.FilePath = getFilePathIndex(filePath, workDir); |
| break; |
| } |
| case IndexUnitDependency::DependencyKind::Record: { |
| depInfo.source.FilePath = getFilePathIndex(dep.getFilePath(), workDir); |
| RecordIndex recIndex = getRecordIndex(dep.getName()); |
| depInfo.source.AssociatedRecords.push_back(recIndex); |
| break; |
| } |
| case IndexUnitDependency::DependencyKind::File: |
| depInfo.source.FilePath = getFilePathIndex(dep.getFilePath(), workDir); |
| } |
| return true; |
| }); |
| |
| unit->Sources.reserve(Deps.size()); |
| for (auto &dep : Deps) { |
| if (!dep.unitName.empty()) { |
| unit->UnitDepends.emplace_back(std::move(dep.unitName)); |
| } else { |
| unit->Sources.push_back(std::move(dep.source)); |
| } |
| } |
| |
| Units.push_back(std::move(unit)); |
| } |
| |
| FilePathIndex Aggregator::getFilePathIndex(StringRef path, StringRef workingDir) { |
| StringRef absPath; |
| SmallString<128> absPathBuf; |
| if (sys::path::is_absolute(path) || workingDir.empty()) { |
| absPath = path; |
| } else { |
| absPathBuf = workingDir; |
| sys::path::append(absPathBuf, path); |
| absPath = absPathBuf.str(); |
| } |
| |
| auto pair = FilePathIndices.insert(std::make_pair(absPath, FilePaths.size())); |
| bool wasInserted = pair.second; |
| if (wasInserted) { |
| FilePaths.push_back(pair.first->first()); |
| } |
| return pair.first->second; |
| } |
| |
| RecordIndex Aggregator::getRecordIndex(StringRef recordFile) { |
| auto pair = RecordIndices.insert(std::make_pair(recordFile, Records.size())); |
| bool wasInserted = pair.second; |
| if (wasInserted) { |
| Records.push_back(processRecord(recordFile)); |
| } |
| return pair.first->second; |
| } |
| |
| std::unique_ptr<RecordInfo> Aggregator::processRecord(StringRef recordFile) { |
| std::string error; |
| auto recordReader = IndexRecordReader(Store, recordFile, error); |
| if (!recordReader) { |
| errs() << "failed reading record file: " << recordFile << '\n'; |
| ::exit(1); |
| } |
| auto record = llvm::make_unique<RecordInfo>(); |
| recordReader.foreachOccurrence([&](IndexRecordOccurrence idxOccur) -> bool { |
| SymbolIndex symIdx = getSymbolIndex(idxOccur.getSymbol()); |
| SymbolInfo &symInfo = Symbols[symIdx]; |
| symInfo.Roles |= idxOccur.getRoles(); |
| SymbolOccurrenceInfo occurInfo; |
| occurInfo.Symbol = symIdx; |
| idxOccur.foreachRelation([&](IndexSymbolRelation rel) -> bool { |
| SymbolIndex relsymIdx = getSymbolIndex(rel.getSymbol()); |
| SymbolInfo &relsymInfo = Symbols[relsymIdx]; |
| relsymInfo.RelatedRoles |= rel.getRoles(); |
| occurInfo.Relations.emplace_back(relsymIdx, rel.getRoles()); |
| return true; |
| }); |
| occurInfo.Roles = idxOccur.getRoles(); |
| std::tie(occurInfo.Line, occurInfo.Column) = idxOccur.getLineCol(); |
| record->Occurrences.push_back(std::move(occurInfo)); |
| return true; |
| }); |
| return record; |
| } |
| |
| SymbolIndex Aggregator::getSymbolIndex(IndexRecordSymbol sym) { |
| auto pair = SymbolIndices.insert(std::make_pair(sym.getUSR(), Symbols.size())); |
| bool wasInserted = pair.second; |
| if (wasInserted) { |
| SymbolInfo symInfo; |
| symInfo.Kind = getSymbolKind(sym.getKind()); |
| symInfo.Lang = getSymbolLanguage(sym.getLanguage()); |
| symInfo.USR = pair.first->first(); |
| symInfo.Name = copyStr(sym.getName()); |
| symInfo.CodegenName = copyStr(sym.getCodegenName()); |
| Symbols.push_back(std::move(symInfo)); |
| } |
| return pair.first->second; |
| } |
| |
| |
| void Aggregator::dumpJSON(raw_ostream &OS) { |
| OS << "{\n"; |
| OS.indent(2) << "\"files\": [\n"; |
| for (unsigned i = 0, e = FilePaths.size(); i != e; ++i) { |
| OS.indent(4) << '\"' << FilePaths[i] << '\"'; |
| if (i < e-1) OS << ','; |
| OS << '\n'; |
| } |
| OS.indent(2) << "],\n"; |
| |
| OS.indent(2) << "\"symbols\": [\n"; |
| for (unsigned i = 0, e = Symbols.size(); i != e; ++i) { |
| OS.indent(4) << "{\n"; |
| SymbolInfo &symInfo = Symbols[i]; |
| OS.indent(6) << "\"kind\": \"" << getSymbolKindString(symInfo.Kind) << "\",\n"; |
| OS.indent(6) << "\"lang\": \"" << getSymbolLanguageString(symInfo.Lang) << "\",\n"; |
| OS.indent(6) << "\"usr\": \"" << symInfo.USR << "\",\n"; |
| OS.indent(6) << "\"name\": \"" << symInfo.Name << "\",\n"; |
| if (!symInfo.CodegenName.empty()) |
| OS.indent(6) << "\"codegen\": \"" << symInfo.CodegenName << "\",\n"; |
| OS.indent(6) << "\"roles\": \""; |
| printSymbolRoles(symInfo.Roles, OS); |
| OS << '\"'; |
| if (symInfo.RelatedRoles != 0) { |
| OS << ",\n"; |
| OS.indent(6) << "\"rel-roles\": \""; |
| printSymbolRoles(symInfo.RelatedRoles, OS); |
| OS << '\"'; |
| } |
| OS << '\n'; |
| OS.indent(4) << "}"; |
| if (i < e-1) OS << ','; |
| OS << '\n'; |
| } |
| OS.indent(2) << "],\n"; |
| |
| OS.indent(2) << "\"records\": [\n"; |
| for (unsigned i = 0, e = Records.size(); i != e; ++i) { |
| OS.indent(4) << "{\n"; |
| RecordInfo &recInfo = *Records[i]; |
| OS.indent(6) << "\"occurrences\": [\n"; |
| for (unsigned oi = 0, oe = recInfo.Occurrences.size(); oi != oe; ++oi) { |
| OS.indent(8) << "{\n"; |
| SymbolOccurrenceInfo &occurInfo = recInfo.Occurrences[oi]; |
| OS.indent(10) << "\"symbol\": " << occurInfo.Symbol << ",\n"; |
| OS.indent(10) << "\"line\": " << occurInfo.Line << ",\n"; |
| OS.indent(10) << "\"col\": " << occurInfo.Column << ",\n"; |
| OS.indent(10) << "\"roles\": \""; |
| printSymbolRoles(occurInfo.Roles, OS); |
| OS << '\"'; |
| if (!occurInfo.Relations.empty()) { |
| OS << ",\n"; |
| OS.indent(10) << "\"relations\": [\n"; |
| for (unsigned ri = 0, re = occurInfo.Relations.size(); ri != re; ++ri) { |
| OS.indent(12) << "{\n"; |
| SymbolRelationInfo &relInfo = occurInfo.Relations[ri]; |
| OS.indent(14) << "\"symbol\": " << relInfo.RelatedSymbol << ",\n"; |
| OS.indent(14) << "\"rel-roles\": \""; |
| printSymbolRoles(relInfo.Roles, OS); |
| OS << "\"\n"; |
| OS.indent(12) << "}"; |
| if (ri < re-1) OS << ','; |
| OS << '\n'; |
| } |
| OS.indent(10) << "]\n"; |
| } |
| OS << '\n'; |
| OS.indent(8) << "}"; |
| if (oi < oe-1) OS << ','; |
| OS << '\n'; |
| } |
| OS.indent(6) << "]\n"; |
| OS.indent(4) << "}"; |
| if (i < e-1) OS << ','; |
| OS << '\n'; |
| } |
| OS.indent(2) << "],\n"; |
| |
| StringMap<size_t> UnitIndicesByName; |
| for (unsigned i = 0, e = Units.size(); i != e; ++i) { |
| UnitInfo &unit = *Units[i]; |
| UnitIndicesByName[unit.Name] = i; |
| } |
| |
| OS.indent(2) << "\"units\": [\n"; |
| for (unsigned i = 0, e = Units.size(); i != e; ++i) { |
| OS.indent(4) << "{\n"; |
| UnitInfo &unit = *Units[i]; |
| OS.indent(6) << "\"triple\": \"" << unit.Triple << "\",\n"; |
| OS.indent(6) << "\"out-file\": " << unit.OutFile << ",\n"; |
| if (!unit.UnitDepends.empty()) { |
| OS.indent(6) << "\"unit-dependencies\": ["; |
| for (unsigned ui = 0, ue = unit.UnitDepends.size(); ui != ue; ++ui) { |
| OS << UnitIndicesByName[unit.UnitDepends[ui]]; |
| if (ui < ue-1) OS << ", "; |
| } |
| OS << "],\n"; |
| } |
| OS.indent(6) << "\"sources\": [\n"; |
| for (unsigned si = 0, se = unit.Sources.size(); si != se; ++si) { |
| OS.indent(8) << "{\n"; |
| UnitSourceInfo &source = unit.Sources[si]; |
| OS.indent(10) << "\"file\": " << source.FilePath; |
| if (!source.AssociatedRecords.empty()) { |
| OS << ",\n"; |
| OS.indent(10) << "\"records\": ["; |
| for (unsigned ri = 0, re = source.AssociatedRecords.size(); ri != re; ++ri) { |
| OS << source.AssociatedRecords[ri]; |
| if (ri < re-1) OS << ", "; |
| } |
| OS << ']'; |
| } |
| OS << '\n'; |
| OS.indent(8) << "}"; |
| if (si < se-1) OS << ','; |
| OS << '\n'; |
| } |
| OS.indent(6) << "]\n"; |
| OS.indent(4) << "}"; |
| if (i < e-1) OS << ','; |
| OS << '\n'; |
| } |
| OS.indent(2) << "]\n"; |
| OS << "}\n"; |
| } |
| |
| |
| bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { |
| std::string error; |
| auto dataStore = IndexStore(StorePath, error); |
| if (!dataStore) { |
| errs() << "error opening store path '" << StorePath << "': " << error << '\n'; |
| return true; |
| } |
| |
| // Explicitely avoid doing any memory cleanup for aggregator since the process |
| // is going to exit when we are done. |
| Aggregator *aggregator = new Aggregator(std::move(dataStore)); |
| bool err = aggregator->process(); |
| if (err) |
| return true; |
| aggregator->dumpJSON(OS); |
| return false; |
| } |
| |
| #else |
| |
| bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) { |
| return true; |
| } |
| #endif |