blob: c7f4136bde7e689ecf962d335c4b4d329aee2b62 [file] [log] [blame]
//===--- 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