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