1 //===-- Serialization.cpp - Binary serialization of index data ------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Serialization.h"
10 #include "Logger.h"
11 #include "RIFF.h"
12 #include "SymbolLocation.h"
13 #include "SymbolOrigin.h"
14 #include "Trace.h"
15 #include "dex/Dex.h"
16 #include "llvm/Support/Compression.h"
17 #include "llvm/Support/Endian.h"
18 #include "llvm/Support/Error.h"
19 
20 namespace clang {
21 namespace clangd {
22 namespace {
23 llvm::Error makeError(const llvm::Twine &Msg) {
24   return llvm::make_error<llvm::StringError>(Msg,
25                                              llvm::inconvertibleErrorCode());
26 }
27 
28 // IO PRIMITIVES
29 // We use little-endian 32 bit ints, sometimes with variable-length encoding.
30 //
31 // Variable-length int encoding (varint) uses the bottom 7 bits of each byte
32 // to encode the number, and the top bit to indicate whether more bytes follow.
33 // e.g. 9a 2f means [0x1a and keep reading, 0x2f and stop].
34 // This represents 0x1a | 0x2f<<7 = 6042.
35 // A 32-bit integer takes 1-5 bytes to encode; small numbers are more compact.
36 
37 // Reads binary data from a StringRef, and keeps track of position.
38 class Reader {
39   const char *Begin, *End;
40   bool Err = false;
41 
42 public:
43   Reader(llvm::StringRef Data) : Begin(Data.begin()), End(Data.end()) {}
44   // The "error" bit is set by reading past EOF or reading invalid data.
45   // When in an error state, reads may return zero values: callers should check.
46   bool err() const { return Err; }
47   // Did we read all the data, or encounter an error?
48   bool eof() const { return Begin == End || Err; }
49   // All the data we didn't read yet.
50   llvm::StringRef rest() const { return llvm::StringRef(Begin, End - Begin); }
51 
52   uint8_t consume8() {
53     if (LLVM_UNLIKELY(Begin == End)) {
54       Err = true;
55       return 0;
56     }
57     return *Begin++;
58   }
59 
60   uint32_t consume32() {
61     if (LLVM_UNLIKELY(Begin + 4 > End)) {
62       Err = true;
63       return 0;
64     }
65     auto Ret = llvm::support::endian::read32le(Begin);
66     Begin += 4;
67     return Ret;
68   }
69 
70   llvm::StringRef consume(int N) {
71     if (LLVM_UNLIKELY(Begin + N > End)) {
72       Err = true;
73       return llvm::StringRef();
74     }
75     llvm::StringRef Ret(Begin, N);
76     Begin += N;
77     return Ret;
78   }
79 
80   uint32_t consumeVar() {
81     constexpr static uint8_t More = 1 << 7;
82     uint8_t B = consume8();
83     if (LLVM_LIKELY(!(B & More)))
84       return B;
85     uint32_t Val = B & ~More;
86     for (int Shift = 7; B & More && Shift < 32; Shift += 7) {
87       B = consume8();
88       Val |= (B & ~More) << Shift;
89     }
90     return Val;
91   }
92 
93   llvm::StringRef consumeString(llvm::ArrayRef<llvm::StringRef> Strings) {
94     auto StringIndex = consumeVar();
95     if (LLVM_UNLIKELY(StringIndex >= Strings.size())) {
96       Err = true;
97       return llvm::StringRef();
98     }
99     return Strings[StringIndex];
100   }
101 
102   SymbolID consumeID() {
103     llvm::StringRef Raw = consume(SymbolID::RawSize); // short if truncated.
104     return LLVM_UNLIKELY(err()) ? SymbolID() : SymbolID::fromRaw(Raw);
105   }
106 };
107 
108 void write32(uint32_t I, llvm::raw_ostream &OS) {
109   char Buf[4];
110   llvm::support::endian::write32le(Buf, I);
111   OS.write(Buf, sizeof(Buf));
112 }
113 
114 void writeVar(uint32_t I, llvm::raw_ostream &OS) {
115   constexpr static uint8_t More = 1 << 7;
116   if (LLVM_LIKELY(I < 1 << 7)) {
117     OS.write(I);
118     return;
119   }
120   for (;;) {
121     OS.write(I | More);
122     I >>= 7;
123     if (I < 1 << 7) {
124       OS.write(I);
125       return;
126     }
127   }
128 }
129 
130 // STRING TABLE ENCODING
131 // Index data has many string fields, and many strings are identical.
132 // We store each string once, and refer to them by index.
133 //
134 // The string table's format is:
135 //   - UncompressedSize : uint32 (or 0 for no compression)
136 //   - CompressedData   : byte[CompressedSize]
137 //
138 // CompressedData is a zlib-compressed byte[UncompressedSize].
139 // It contains a sequence of null-terminated strings, e.g. "foo\0bar\0".
140 // These are sorted to improve compression.
141 
142 // Maps each string to a canonical representation.
143 // Strings remain owned externally (e.g. by SymbolSlab).
144 class StringTableOut {
145   llvm::DenseSet<llvm::StringRef> Unique;
146   std::vector<llvm::StringRef> Sorted;
147   // Since strings are interned, look up can be by pointer.
148   llvm::DenseMap<std::pair<const char *, size_t>, unsigned> Index;
149 
150 public:
151   StringTableOut() {
152     // Ensure there's at least one string in the table.
153     // Table size zero is reserved to indicate no compression.
154     Unique.insert("");
155   }
156   // Add a string to the table. Overwrites S if an identical string exists.
157   void intern(llvm::StringRef &S) { S = *Unique.insert(S).first; };
158   // Finalize the table and write it to OS. No more strings may be added.
159   void finalize(llvm::raw_ostream &OS) {
160     Sorted = {Unique.begin(), Unique.end()};
161     llvm::sort(Sorted);
162     for (unsigned I = 0; I < Sorted.size(); ++I)
163       Index.try_emplace({Sorted[I].data(), Sorted[I].size()}, I);
164 
165     std::string RawTable;
166     for (llvm::StringRef S : Sorted) {
167       RawTable.append(S);
168       RawTable.push_back(0);
169     }
170     if (llvm::zlib::isAvailable()) {
171       llvm::SmallString<1> Compressed;
172       llvm::cantFail(llvm::zlib::compress(RawTable, Compressed));
173       write32(RawTable.size(), OS);
174       OS << Compressed;
175     } else {
176       write32(0, OS); // No compression.
177       OS << RawTable;
178     }
179   }
180   // Get the ID of an string, which must be interned. Table must be finalized.
181   unsigned index(llvm::StringRef S) const {
182     assert(!Sorted.empty() && "table not finalized");
183     assert(Index.count({S.data(), S.size()}) && "string not interned");
184     return Index.find({S.data(), S.size()})->second;
185   }
186 };
187 
188 struct StringTableIn {
189   llvm::BumpPtrAllocator Arena;
190   std::vector<llvm::StringRef> Strings;
191 };
192 
193 llvm::Expected<StringTableIn> readStringTable(llvm::StringRef Data) {
194   Reader R(Data);
195   size_t UncompressedSize = R.consume32();
196   if (R.err())
197     return makeError("Truncated string table");
198 
199   llvm::StringRef Uncompressed;
200   llvm::SmallString<1> UncompressedStorage;
201   if (UncompressedSize == 0) // No compression
202     Uncompressed = R.rest();
203   else {
204     if (llvm::Error E = llvm::zlib::uncompress(R.rest(), UncompressedStorage,
205                                                UncompressedSize))
206       return std::move(E);
207     Uncompressed = UncompressedStorage;
208   }
209 
210   StringTableIn Table;
211   llvm::StringSaver Saver(Table.Arena);
212   R = Reader(Uncompressed);
213   for (Reader R(Uncompressed); !R.eof();) {
214     auto Len = R.rest().find(0);
215     if (Len == llvm::StringRef::npos)
216       return makeError("Bad string table: not null terminated");
217     Table.Strings.push_back(Saver.save(R.consume(Len)));
218     R.consume8();
219   }
220   if (R.err())
221     return makeError("Truncated string table");
222   return std::move(Table);
223 }
224 
225 // SYMBOL ENCODING
226 // Each field of clangd::Symbol is encoded in turn (see implementation).
227 //  - StringRef fields encode as varint (index into the string table)
228 //  - enums encode as the underlying type
229 //  - most numbers encode as varint
230 
231 void writeLocation(const SymbolLocation &Loc, const StringTableOut &Strings,
232                    llvm::raw_ostream &OS) {
233   writeVar(Strings.index(Loc.FileURI), OS);
234   for (const auto &Endpoint : {Loc.Start, Loc.End}) {
235     writeVar(Endpoint.line(), OS);
236     writeVar(Endpoint.column(), OS);
237   }
238 }
239 
240 SymbolLocation readLocation(Reader &Data,
241                             llvm::ArrayRef<llvm::StringRef> Strings) {
242   SymbolLocation Loc;
243   Loc.FileURI = Data.consumeString(Strings).data();
244   for (auto *Endpoint : {&Loc.Start, &Loc.End}) {
245     Endpoint->setLine(Data.consumeVar());
246     Endpoint->setColumn(Data.consumeVar());
247   }
248   return Loc;
249 }
250 
251 IncludeGraphNode readIncludeGraphNode(Reader &Data,
252                                       llvm::ArrayRef<llvm::StringRef> Strings) {
253   IncludeGraphNode IGN;
254   IGN.IsTU = Data.consume8();
255   IGN.URI = Data.consumeString(Strings);
256   llvm::StringRef Digest = Data.consume(IGN.Digest.size());
257   std::copy(Digest.bytes_begin(), Digest.bytes_end(), IGN.Digest.begin());
258   IGN.DirectIncludes.resize(Data.consumeVar());
259   for (llvm::StringRef &Include : IGN.DirectIncludes)
260     Include = Data.consumeString(Strings);
261   return IGN;
262 }
263 
264 void writeIncludeGraphNode(const IncludeGraphNode &IGN,
265                            const StringTableOut &Strings,
266                            llvm::raw_ostream &OS) {
267   OS.write(IGN.IsTU);
268   writeVar(Strings.index(IGN.URI), OS);
269   llvm::StringRef Hash(reinterpret_cast<const char *>(IGN.Digest.data()),
270                        IGN.Digest.size());
271   OS << Hash;
272   writeVar(IGN.DirectIncludes.size(), OS);
273   for (llvm::StringRef Include : IGN.DirectIncludes)
274     writeVar(Strings.index(Include), OS);
275 }
276 
277 void writeSymbol(const Symbol &Sym, const StringTableOut &Strings,
278                  llvm::raw_ostream &OS) {
279   OS << Sym.ID.raw(); // TODO: once we start writing xrefs and posting lists,
280                       // symbol IDs should probably be in a string table.
281   OS.write(static_cast<uint8_t>(Sym.SymInfo.Kind));
282   OS.write(static_cast<uint8_t>(Sym.SymInfo.Lang));
283   writeVar(Strings.index(Sym.Name), OS);
284   writeVar(Strings.index(Sym.Scope), OS);
285   writeVar(Strings.index(Sym.TemplateSpecializationArgs), OS);
286   writeLocation(Sym.Definition, Strings, OS);
287   writeLocation(Sym.CanonicalDeclaration, Strings, OS);
288   writeVar(Sym.References, OS);
289   OS.write(static_cast<uint8_t>(Sym.Flags));
290   OS.write(static_cast<uint8_t>(Sym.Origin));
291   writeVar(Strings.index(Sym.Signature), OS);
292   writeVar(Strings.index(Sym.CompletionSnippetSuffix), OS);
293   writeVar(Strings.index(Sym.Documentation), OS);
294   writeVar(Strings.index(Sym.ReturnType), OS);
295   writeVar(Strings.index(Sym.Type), OS);
296 
297   auto WriteInclude = [&](const Symbol::IncludeHeaderWithReferences &Include) {
298     writeVar(Strings.index(Include.IncludeHeader), OS);
299     writeVar(Include.References, OS);
300   };
301   writeVar(Sym.IncludeHeaders.size(), OS);
302   for (const auto &Include : Sym.IncludeHeaders)
303     WriteInclude(Include);
304 }
305 
306 Symbol readSymbol(Reader &Data, llvm::ArrayRef<llvm::StringRef> Strings) {
307   Symbol Sym;
308   Sym.ID = Data.consumeID();
309   Sym.SymInfo.Kind = static_cast<index::SymbolKind>(Data.consume8());
310   Sym.SymInfo.Lang = static_cast<index::SymbolLanguage>(Data.consume8());
311   Sym.Name = Data.consumeString(Strings);
312   Sym.Scope = Data.consumeString(Strings);
313   Sym.TemplateSpecializationArgs = Data.consumeString(Strings);
314   Sym.Definition = readLocation(Data, Strings);
315   Sym.CanonicalDeclaration = readLocation(Data, Strings);
316   Sym.References = Data.consumeVar();
317   Sym.Flags = static_cast<Symbol::SymbolFlag>(Data.consumeVar());
318   Sym.Origin = static_cast<SymbolOrigin>(Data.consumeVar());
319   Sym.Signature = Data.consumeString(Strings);
320   Sym.CompletionSnippetSuffix = Data.consumeString(Strings);
321   Sym.Documentation = Data.consumeString(Strings);
322   Sym.ReturnType = Data.consumeString(Strings);
323   Sym.Type = Data.consumeString(Strings);
324   Sym.IncludeHeaders.resize(Data.consumeVar());
325   for (auto &I : Sym.IncludeHeaders) {
326     I.IncludeHeader = Data.consumeString(Strings);
327     I.References = Data.consumeVar();
328   }
329   return Sym;
330 }
331 
332 // REFS ENCODING
333 // A refs section has data grouped by Symbol. Each symbol has:
334 //  - SymbolID: 8 bytes
335 //  - NumRefs: varint
336 //  - Ref[NumRefs]
337 // Fields of Ref are encoded in turn, see implementation.
338 
339 void writeRefs(const SymbolID &ID, llvm::ArrayRef<Ref> Refs,
340                const StringTableOut &Strings, llvm::raw_ostream &OS) {
341   OS << ID.raw();
342   writeVar(Refs.size(), OS);
343   for (const auto &Ref : Refs) {
344     OS.write(static_cast<unsigned char>(Ref.Kind));
345     writeLocation(Ref.Location, Strings, OS);
346   }
347 }
348 
349 std::pair<SymbolID, std::vector<Ref>>
350 readRefs(Reader &Data, llvm::ArrayRef<llvm::StringRef> Strings) {
351   std::pair<SymbolID, std::vector<Ref>> Result;
352   Result.first = Data.consumeID();
353   Result.second.resize(Data.consumeVar());
354   for (auto &Ref : Result.second) {
355     Ref.Kind = static_cast<RefKind>(Data.consume8());
356     Ref.Location = readLocation(Data, Strings);
357   }
358   return Result;
359 }
360 
361 // FILE ENCODING
362 // A file is a RIFF chunk with type 'CdIx'.
363 // It contains the sections:
364 //   - meta: version number
365 //   - srcs: information related to include graph
366 //   - stri: string table
367 //   - symb: symbols
368 //   - refs: references to symbols
369 
370 // The current versioning scheme is simple - non-current versions are rejected.
371 // If you make a breaking change, bump this version number to invalidate stored
372 // data. Later we may want to support some backward compatibility.
373 constexpr static uint32_t Version = 9;
374 
375 llvm::Expected<IndexFileIn> readRIFF(llvm::StringRef Data) {
376   auto RIFF = riff::readFile(Data);
377   if (!RIFF)
378     return RIFF.takeError();
379   if (RIFF->Type != riff::fourCC("CdIx"))
380     return makeError("wrong RIFF type");
381   llvm::StringMap<llvm::StringRef> Chunks;
382   for (const auto &Chunk : RIFF->Chunks)
383     Chunks.try_emplace(llvm::StringRef(Chunk.ID.data(), Chunk.ID.size()),
384                        Chunk.Data);
385 
386   for (llvm::StringRef RequiredChunk : {"meta", "stri"})
387     if (!Chunks.count(RequiredChunk))
388       return makeError("missing required chunk " + RequiredChunk);
389 
390   Reader Meta(Chunks.lookup("meta"));
391   if (Meta.consume32() != Version)
392     return makeError("wrong version");
393 
394   auto Strings = readStringTable(Chunks.lookup("stri"));
395   if (!Strings)
396     return Strings.takeError();
397 
398   IndexFileIn Result;
399   if (Chunks.count("srcs")) {
400     Reader SrcsReader(Chunks.lookup("srcs"));
401     Result.Sources.emplace();
402     while (!SrcsReader.eof()) {
403       auto IGN = readIncludeGraphNode(SrcsReader, Strings->Strings);
404       auto Entry = Result.Sources->try_emplace(IGN.URI).first;
405       Entry->getValue() = std::move(IGN);
406       // We change all the strings inside the structure to point at the keys in
407       // the map, since it is the only copy of the string that's going to live.
408       Entry->getValue().URI = Entry->getKey();
409       for (auto &Include : Entry->getValue().DirectIncludes)
410         Include = Result.Sources->try_emplace(Include).first->getKey();
411     }
412     if (SrcsReader.err())
413       return makeError("malformed or truncated include uri");
414   }
415 
416   if (Chunks.count("symb")) {
417     Reader SymbolReader(Chunks.lookup("symb"));
418     SymbolSlab::Builder Symbols;
419     while (!SymbolReader.eof())
420       Symbols.insert(readSymbol(SymbolReader, Strings->Strings));
421     if (SymbolReader.err())
422       return makeError("malformed or truncated symbol");
423     Result.Symbols = std::move(Symbols).build();
424   }
425   if (Chunks.count("refs")) {
426     Reader RefsReader(Chunks.lookup("refs"));
427     RefSlab::Builder Refs;
428     while (!RefsReader.eof()) {
429       auto RefsBundle = readRefs(RefsReader, Strings->Strings);
430       for (const auto &Ref : RefsBundle.second) // FIXME: bulk insert?
431         Refs.insert(RefsBundle.first, Ref);
432     }
433     if (RefsReader.err())
434       return makeError("malformed or truncated refs");
435     Result.Refs = std::move(Refs).build();
436   }
437   return std::move(Result);
438 }
439 
440 template <class Callback>
441 void visitStrings(IncludeGraphNode &IGN, const Callback &CB) {
442   CB(IGN.URI);
443   for (llvm::StringRef &Include : IGN.DirectIncludes)
444     CB(Include);
445 }
446 
447 void writeRIFF(const IndexFileOut &Data, llvm::raw_ostream &OS) {
448   assert(Data.Symbols && "An index file without symbols makes no sense!");
449   riff::File RIFF;
450   RIFF.Type = riff::fourCC("CdIx");
451 
452   llvm::SmallString<4> Meta;
453   {
454     llvm::raw_svector_ostream MetaOS(Meta);
455     write32(Version, MetaOS);
456   }
457   RIFF.Chunks.push_back({riff::fourCC("meta"), Meta});
458 
459   StringTableOut Strings;
460   std::vector<Symbol> Symbols;
461   for (const auto &Sym : *Data.Symbols) {
462     Symbols.emplace_back(Sym);
463     visitStrings(Symbols.back(),
464                  [&](llvm::StringRef &S) { Strings.intern(S); });
465   }
466   std::vector<IncludeGraphNode> Sources;
467   if (Data.Sources)
468     for (const auto &Source : *Data.Sources) {
469       Sources.push_back(Source.getValue());
470       visitStrings(Sources.back(),
471                    [&](llvm::StringRef &S) { Strings.intern(S); });
472     }
473 
474   std::vector<std::pair<SymbolID, std::vector<Ref>>> Refs;
475   if (Data.Refs) {
476     for (const auto &Sym : *Data.Refs) {
477       Refs.emplace_back(Sym);
478       for (auto &Ref : Refs.back().second) {
479         llvm::StringRef File = Ref.Location.FileURI;
480         Strings.intern(File);
481         Ref.Location.FileURI = File.data();
482       }
483     }
484   }
485 
486   std::string StringSection;
487   {
488     llvm::raw_string_ostream StringOS(StringSection);
489     Strings.finalize(StringOS);
490   }
491   RIFF.Chunks.push_back({riff::fourCC("stri"), StringSection});
492 
493   std::string SymbolSection;
494   {
495     llvm::raw_string_ostream SymbolOS(SymbolSection);
496     for (const auto &Sym : Symbols)
497       writeSymbol(Sym, Strings, SymbolOS);
498   }
499   RIFF.Chunks.push_back({riff::fourCC("symb"), SymbolSection});
500 
501   std::string RefsSection;
502   if (Data.Refs) {
503     {
504       llvm::raw_string_ostream RefsOS(RefsSection);
505       for (const auto &Sym : Refs)
506         writeRefs(Sym.first, Sym.second, Strings, RefsOS);
507     }
508     RIFF.Chunks.push_back({riff::fourCC("refs"), RefsSection});
509   }
510 
511   std::string SrcsSection;
512   {
513     {
514       llvm::raw_string_ostream SrcsOS(SrcsSection);
515       for (const auto &SF : Sources)
516         writeIncludeGraphNode(SF, Strings, SrcsOS);
517     }
518     RIFF.Chunks.push_back({riff::fourCC("srcs"), SrcsSection});
519   }
520 
521   OS << RIFF;
522 }
523 
524 } // namespace
525 
526 // Defined in YAMLSerialization.cpp.
527 void writeYAML(const IndexFileOut &, llvm::raw_ostream &);
528 llvm::Expected<IndexFileIn> readYAML(llvm::StringRef);
529 
530 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O) {
531   switch (O.Format) {
532   case IndexFileFormat::RIFF:
533     writeRIFF(O, OS);
534     break;
535   case IndexFileFormat::YAML:
536     writeYAML(O, OS);
537     break;
538   }
539   return OS;
540 }
541 
542 llvm::Expected<IndexFileIn> readIndexFile(llvm::StringRef Data) {
543   if (Data.startswith("RIFF")) {
544     return readRIFF(Data);
545   } else if (auto YAMLContents = readYAML(Data)) {
546     return std::move(*YAMLContents);
547   } else {
548     return makeError("Not a RIFF file and failed to parse as YAML: " +
549                      llvm::toString(YAMLContents.takeError()));
550   }
551 }
552 
553 std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef SymbolFilename,
554                                        bool UseDex) {
555   trace::Span OverallTracer("LoadIndex");
556   auto Buffer = llvm::MemoryBuffer::getFile(SymbolFilename);
557   if (!Buffer) {
558     llvm::errs() << "Can't open " << SymbolFilename << "\n";
559     return nullptr;
560   }
561 
562   SymbolSlab Symbols;
563   RefSlab Refs;
564   {
565     trace::Span Tracer("ParseIndex");
566     if (auto I = readIndexFile(Buffer->get()->getBuffer())) {
567       if (I->Symbols)
568         Symbols = std::move(*I->Symbols);
569       if (I->Refs)
570         Refs = std::move(*I->Refs);
571     } else {
572       llvm::errs() << "Bad Index: " << llvm::toString(I.takeError()) << "\n";
573       return nullptr;
574     }
575   }
576 
577   size_t NumSym = Symbols.size();
578   size_t NumRefs = Refs.numRefs();
579 
580   trace::Span Tracer("BuildIndex");
581   auto Index = UseDex ? dex::Dex::build(std::move(Symbols), std::move(Refs))
582                       : MemIndex::build(std::move(Symbols), std::move(Refs));
583   vlog("Loaded {0} from {1} with estimated memory usage {2} bytes\n"
584        "  - number of symbols: {3}\n"
585        "  - number of refs: {4}\n",
586        UseDex ? "Dex" : "MemIndex", SymbolFilename,
587        Index->estimateMemoryUsage(), NumSym, NumRefs);
588   return Index;
589 }
590 
591 } // namespace clangd
592 } // namespace clang
593