From 5184e3a13d9b184bbd4b0cae7a717bfb077c82c7 Mon Sep 17 00:00:00 2001 From: "garo (they/them)" Date: Mon, 16 Nov 2020 10:17:47 -0500 Subject: [PATCH 01/19] bugfix: pick path under project root when possible (#28) * bugfix: pick path under project root when possible * remove unused import --- src/index/Merge.cpp | 146 +++--------------------------------- src/index/Merge.h | 34 +-------- src/indexer/IndexerMain.cpp | 7 +- 3 files changed, 17 insertions(+), 170 deletions(-) diff --git a/src/index/Merge.cpp b/src/index/Merge.cpp index 0cef7dc763..6a6889836d 100644 --- a/src/index/Merge.cpp +++ b/src/index/Merge.cpp @@ -22,139 +22,10 @@ namespace clang { namespace clangd { -// FIXME: Deleted symbols in dirty files are still returned (from Static). -// To identify these eliminate these, we should: -// - find the generating file from each Symbol which is Static-only -// - ask Dynamic if it has that file (needs new SymbolIndex method) -// - if so, drop the Symbol. -bool MergedIndex::fuzzyFind( - const FuzzyFindRequest &Req, - llvm::function_ref Callback) const { - // We can't step through both sources in parallel. So: - // 1) query all dynamic symbols, slurping results into a slab - // 2) query the static symbols, for each one: - // a) if it's not in the dynamic slab, yield it directly - // b) if it's in the dynamic slab, merge it and yield the result - // 3) now yield all the dynamic symbols we haven't processed. - trace::Span Tracer("MergedIndex fuzzyFind"); - bool More = false; // We'll be incomplete if either source was. - SymbolSlab::Builder DynB; - unsigned DynamicCount = 0; - unsigned StaticCount = 0; - unsigned MergedCount = 0; - More |= Dynamic->fuzzyFind(Req, [&](const Symbol &S) { - ++DynamicCount; - DynB.insert(S); - }); - SymbolSlab Dyn = std::move(DynB).build(); - - llvm::DenseSet SeenDynamicSymbols; - More |= Static->fuzzyFind(Req, [&](const Symbol &S) { - auto DynS = Dyn.find(S.ID); - ++StaticCount; - if (DynS == Dyn.end()) - return Callback(S); - ++MergedCount; - SeenDynamicSymbols.insert(S.ID); - Callback(mergeSymbol(*DynS, S)); - }); - SPAN_ATTACH(Tracer, "dynamic", DynamicCount); - SPAN_ATTACH(Tracer, "static", StaticCount); - SPAN_ATTACH(Tracer, "merged", MergedCount); - for (const Symbol &S : Dyn) - if (!SeenDynamicSymbols.count(S.ID)) - Callback(S); - return More; -} - -void MergedIndex::lookup( - const LookupRequest &Req, - llvm::function_ref Callback) const { - trace::Span Tracer("MergedIndex lookup"); - SymbolSlab::Builder B; - - Dynamic->lookup(Req, [&](const Symbol &S) { B.insert(S); }); - - auto RemainingIDs = Req.IDs; - Static->lookup(Req, [&](const Symbol &S) { - const Symbol *Sym = B.find(S.ID); - RemainingIDs.erase(S.ID); - if (!Sym) - Callback(S); - else - Callback(mergeSymbol(*Sym, S)); - }); - for (const auto &ID : RemainingIDs) - if (const Symbol *Sym = B.find(ID)) - Callback(*Sym); -} - -bool MergedIndex::refs(const RefsRequest &Req, - llvm::function_ref Callback) const { - trace::Span Tracer("MergedIndex refs"); - bool More = false; - uint32_t Remaining = - Req.Limit.getValueOr(std::numeric_limits::max()); - // We don't want duplicated refs from the static/dynamic indexes, - // and we can't reliably deduplicate them because offsets may differ slightly. - // We consider the dynamic index authoritative and report all its refs, - // and only report static index refs from other files. - // - // FIXME: The heuristic fails if the dynamic index contains a file, but all - // refs were removed (we will report stale ones from the static index). - // Ultimately we should explicit check which index has the file instead. - llvm::StringSet<> DynamicIndexFileURIs; - More |= Dynamic->refs(Req, [&](const Ref &O) { - DynamicIndexFileURIs.insert(O.Location.FileURI); - Callback(O); - assert(Remaining != 0); - --Remaining; - }); - if (Remaining == 0 && More) - return More; - // We return less than Req.Limit if static index returns more refs for dirty - // files. - bool StaticHadMore = Static->refs(Req, [&](const Ref &O) { - if (DynamicIndexFileURIs.count(O.Location.FileURI)) - return; // ignore refs that have been seen from dynamic index. - if (Remaining == 0) { - More = true; - return; - } - --Remaining; - Callback(O); - }); - return More || StaticHadMore; -} - -void MergedIndex::relations( - const RelationsRequest &Req, - llvm::function_ref Callback) const { - uint32_t Remaining = - Req.Limit.getValueOr(std::numeric_limits::max()); - // Return results from both indexes but avoid duplicates. - // We might return stale relations from the static index; - // we don't currently have a good way of identifying them. - llvm::DenseSet> SeenRelations; - Dynamic->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) { - Callback(Subject, Object); - SeenRelations.insert(std::make_pair(Subject, Object.ID)); - --Remaining; - }); - if (Remaining == 0) - return; - Static->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) { - if (Remaining > 0 && - !SeenRelations.count(std::make_pair(Subject, Object.ID))) { - --Remaining; - Callback(Subject, Object); - } - }); -} - // Returns true if \p L is (strictly) preferred to \p R (e.g. by file paths). If // neither is preferred, this returns false. -bool prefer(const SymbolLocation &L, const SymbolLocation &R) { +bool prefer(const SymbolLocation &L, const SymbolLocation &R, + const std::string &ProjectRoot) { if (!L) return false; if (!R) @@ -166,10 +37,15 @@ bool prefer(const SymbolLocation &L, const SymbolLocation &R) { return llvm::StringRef(Loc.FileURI).endswith(Suffix); }); }; - return HasCodeGenSuffix(L) && !HasCodeGenSuffix(R); + auto InProject = [ProjectRoot](const SymbolLocation &Loc) { + return Loc.FileURI && llvm::StringRef(Loc.FileURI).startswith(ProjectRoot); + }; + + return (HasCodeGenSuffix(L) && !HasCodeGenSuffix(R)) || (InProject(L) && !InProject(R)); } -Symbol mergeSymbol(const Symbol &L, const Symbol &R) { +Symbol mergeSymbol(const Symbol &L, const Symbol &R, + const std::string &ProjectRoot) { assert(L.ID == R.ID); // We prefer information from TUs that saw the definition. // Classes: this is the def itself. Functions: hopefully the header decl. @@ -184,9 +60,9 @@ Symbol mergeSymbol(const Symbol &L, const Symbol &R) { const Symbol &O = PreferR ? L : R; // The "other" less-preferred symbol. // Only use locations in \p O if it's (strictly) preferred. - if (prefer(O.CanonicalDeclaration, S.CanonicalDeclaration)) + if (prefer(O.CanonicalDeclaration, S.CanonicalDeclaration, ProjectRoot)) S.CanonicalDeclaration = O.CanonicalDeclaration; - if (prefer(O.Definition, S.Definition)) + if (prefer(O.Definition, S.Definition, ProjectRoot)) S.Definition = O.Definition; S.References += O.References; if (S.Signature == "") diff --git a/src/index/Merge.h b/src/index/Merge.h index 3288aef120..b383b09444 100644 --- a/src/index/Merge.h +++ b/src/index/Merge.h @@ -17,38 +17,8 @@ namespace clangd { // Merge symbols L and R, preferring data from L in case of conflict. // The two symbols must have the same ID. // Returned symbol may contain data owned by either source. -Symbol mergeSymbol(const Symbol &L, const Symbol &R); - -// MergedIndex is a composite index based on two provided Indexes: -// - the Dynamic index covers few files, but is relatively up-to-date. -// - the Static index covers a bigger set of files, but is relatively stale. -// The returned index attempts to combine results, and avoid duplicates. -// -// FIXME: We don't have a mechanism in Index to track deleted symbols and -// refs in dirty files, so the merged index may return stale symbols -// and refs from Static index. -class MergedIndex : public SymbolIndex { - const SymbolIndex *Dynamic, *Static; - -public: - // The constructor does not access the symbols. - // It's safe to inherit from this class and pass pointers to derived members. - MergedIndex(const SymbolIndex *Dynamic, const SymbolIndex *Static) - : Dynamic(Dynamic), Static(Static) {} - - bool fuzzyFind(const FuzzyFindRequest &, - llvm::function_ref) const override; - void lookup(const LookupRequest &, - llvm::function_ref) const override; - bool refs(const RefsRequest &, - llvm::function_ref) const override; - void relations(const RelationsRequest &, - llvm::function_ref) - const override; - size_t estimateMemoryUsage() const override { - return Dynamic->estimateMemoryUsage() + Static->estimateMemoryUsage(); - } -}; +Symbol mergeSymbol(const Symbol &L, const Symbol &R, + const std::string &ProjectRoot); } // namespace clangd } // namespace clang diff --git a/src/indexer/IndexerMain.cpp b/src/indexer/IndexerMain.cpp index 0621f5e0c4..e2bbe2cdd8 100644 --- a/src/indexer/IndexerMain.cpp +++ b/src/indexer/IndexerMain.cpp @@ -55,7 +55,7 @@ static cl::opt DebugArg("debug", cl::desc("Enable verbose debug output."), class IndexActionFactory : public FrontendActionFactory { public: - IndexActionFactory(clang::clangd::IndexFileIn &Result) : Result(Result) {} + IndexActionFactory(clang::clangd::IndexFileIn &Result, std::string &ProjectRoot) : Result(Result), ProjectRoot(ProjectRoot) {} std::unique_ptr create() override { clang::clangd::SymbolCollector::Options Opts; @@ -76,7 +76,7 @@ class IndexActionFactory : public FrontendActionFactory { std::lock_guard Lock(SymbolsMu); for (const auto &Sym : S) { if (const auto *Existing = Symbols.find(Sym.ID)) - Symbols.insert(mergeSymbol(*Existing, Sym)); + Symbols.insert(mergeSymbol(*Existing, Sym, ProjectRoot)); else Symbols.insert(Sym); } @@ -112,6 +112,7 @@ class IndexActionFactory : public FrontendActionFactory { clang::clangd::SymbolSlab::Builder Symbols; clang::clangd::RefSlab::Builder Refs; clang::clangd::RelationSlab::Builder Relations; + std::string &ProjectRoot; }; int main(int argc, const char **argv) { @@ -141,7 +142,7 @@ int main(int argc, const char **argv) { AllTUsToolExecutor Executor(OptionsParser.getCompilations(), 0); auto Err = - Executor.execute(std::make_unique(Data), Adjuster); + Executor.execute(std::make_unique(Data, ProjectRoot), Adjuster); if (Err) { } From 12aa87544250bd3f146cd4342689d0762d1dad54 Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Wed, 18 Nov 2020 21:24:19 +0000 Subject: [PATCH 02/19] Added check for invalid utf8 doc string and cleaning --- src/index/LSIFSerialization.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/index/LSIFSerialization.cpp b/src/index/LSIFSerialization.cpp index 6ff6774de9..57d8a0a6a1 100644 --- a/src/index/LSIFSerialization.cpp +++ b/src/index/LSIFSerialization.cpp @@ -254,8 +254,13 @@ void writeHoverResult(LSIFMeta &Meta, const clang::clangd::Symbol &Sym, JSONOut.attribute("value", OHover.str()); }); - if (Sym.Documentation.data() && !Sym.Documentation.empty()) - JSONOut.value(Sym.Documentation); + if (Sym.Documentation.data() && !Sym.Documentation.empty()) { + auto docString = Sym.Documentation; + if(!llvm::json::isUTF8(docString)) { + docString = llvm::StringRef(llvm::json::fixUTF8(Sym.Documentation)); + } + JSONOut.value(docString); + } }); }); }); From 234bb8196af18a300f3195cb45c2cd976954279c Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Mon, 30 Nov 2020 17:32:33 +0000 Subject: [PATCH 03/19] Added .gitignore and updated linux kernel instructions --- .gitignore | 8 ++++++++ docs/compdb.md | 6 +++--- docs/examples.md | 8 ++++---- 3 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..cebb9213d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +massif.out.* +vgcore.* +valgrind* +.clangd +.cache +bin +build +cmake-build-debug \ No newline at end of file diff --git a/docs/compdb.md b/docs/compdb.md index 41309010c4..4dd1e37d86 100644 --- a/docs/compdb.md +++ b/docs/compdb.md @@ -36,11 +36,11 @@ http_archive( ``` Then you can run: -``` +```bash bazel build \ --aspects=@bazel_compdb//:aspects.bzl%compilation_database_aspect \ - --output_groups=compdb_files,header_files `# this should include any generated outputs needed for cpp compilation` \ - $(bazel query 'kind("cc_(library|binary|test|inc_library|proto_library)", //...)')' + --output_groups=compdb_files,header_files \ # this should include any generated outputs needed for cpp compilation + $(bazel query 'kind("cc_(library|binary|test|inc_library|proto_library)", //...)') ``` The bazel query might look different for your project. diff --git a/docs/examples.md b/docs/examples.md index 442b0c5ff6..6f30b04da3 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -9,8 +9,8 @@ This page provides examples of generating compile_commands.json files for differ Once you've installed the kernel dependencies (you can use the table in `Documentation/process/changes.rst`), run the following commands from the repository root: ```sh make allyesconfig -make CC=clang-10 HOSTCC=clang-10 `# replace with your clang version` -scripts/gen_compile_commands.py +make CC=clang-10 HOSTCC=clang-10 # replace with your clang version +scripts/clang-tools/gen_compile_commands.py lsif-clang compile_commands.json ``` @@ -18,7 +18,7 @@ lsif-clang compile_commands.json Use the following steps: ```sh -./bazel/setup_clang.sh /usr/lib/llvm-10 `# or wherever your llvm installation lives` +./bazel/setup_clang.sh /usr/lib/llvm-10 # or wherever your llvm installation lives echo 'build --config=clang' > user.bazelrc TEST_TMPDIR=/tmp tools/gen_compilation_database.py --include_headers --run_bazel_build lsif-clang compile_commands.json @@ -28,6 +28,6 @@ lsif-clang compile_commands.json Install the [Ninja build tool](https://ninja-build.org/), and run the following commands from the repository root: ```sh cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -Dgrpc_BUILD_TESTS=ON -G Ninja -ninja -C build $(egrep -e '^build[^:]+.pb.(cc|h|c|cpp|inc|hpp)[: ]' build/build.ninja | awk '{print $2}') `# generate pb` +ninja -C build $(egrep -e '^build[^:]+.pb.(cc|h|c|cpp|inc|hpp)[: ]' build/build.ninja | awk '{print $2}') # generate pb lsif-clang build/compile_commands.json ``` From 5bd7e30d7697f5a8c2d8886cc8aa7a173f951276 Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Wed, 2 Dec 2020 19:33:23 +0000 Subject: [PATCH 04/19] Dockerized with Github Action --- .dockerignore | 5 +++++ .github/workflows/release.yml | 28 ++++++++++++++++++++++++++++ Dockerfile | 17 +++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/release.yml create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..0be97f5fe9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +massif.out.* +.vscode +.devcontainer +build/ +bin/ \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..bcecbd798a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,28 @@ +name: release + +on: + push: + branches: + - llvm-10.0.0-lsif-clang + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Unshallow + run: git fetch --prune --unshallow && git submodule update --init --recursive + - name: Docker login + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + with: + push: true + tags: sourcegraph/lsif-clang:latest + - name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..95f1b9fb73 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM ubuntu:20.04 as build + +RUN apt update && apt install -y llvm-10 clang clang-10 libclang-10-dev cmake + +WORKDIR /lsif-clang + +COPY . . + +RUN cmake -B build && make -C build -j$(nproc) + +FROM ubuntu:20.04 + +RUN apt update && apt install -y llvm-10 cmake + +COPY --from=build /lsif-clang/bin/lsif-clang /usr/local/bin/lsif-clang + +ENTRYPOINT [ "lsif-clang" ] \ No newline at end of file From a350b9264a406fbad366251bc6c39c6f372384ca Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Wed, 2 Dec 2020 19:36:38 +0000 Subject: [PATCH 05/19] test commit for workflow --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cebb9213d4..09479aee72 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ valgrind* .cache bin build -cmake-build-debug \ No newline at end of file +cmake-build-debug* \ No newline at end of file From 85a21810ee41691e17d21650bfe903f139b05571 Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Wed, 2 Dec 2020 19:37:12 +0000 Subject: [PATCH 06/19] Fixed branch name --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bcecbd798a..a5faf9d8d5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: release on: push: branches: - - llvm-10.0.0-lsif-clang + - llvmorg-10.0.0-lsif-clang jobs: goreleaser: From 91bf298baf5fe073daaf5a5244a84892ce9e4612 Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Wed, 2 Dec 2020 19:41:00 +0000 Subject: [PATCH 07/19] auto push not supported OOTB --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a5faf9d8d5..faa1655973 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,13 +6,14 @@ on: - llvmorg-10.0.0-lsif-clang jobs: - goreleaser: + release: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Unshallow run: git fetch --prune --unshallow && git submodule update --init --recursive + - uses: docker/setup-buildx-action@v1 - name: Docker login uses: docker/login-action@v1 with: From ba900cca9ebcf87f8e9cb136e36245c6ddaf571c Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Wed, 2 Dec 2020 19:56:56 +0000 Subject: [PATCH 08/19] Smaller image by only shipping libllvm10 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 95f1b9fb73..8248213744 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN cmake -B build && make -C build -j$(nproc) FROM ubuntu:20.04 -RUN apt update && apt install -y llvm-10 cmake +RUN apt update && apt install -y libllvm10 cmake COPY --from=build /lsif-clang/bin/lsif-clang /usr/local/bin/lsif-clang From 684b21be94cb9c1a9c2206c82afd63ddf5167aae Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Mon, 4 Jan 2021 14:57:08 +0000 Subject: [PATCH 09/19] Bump dockerfile to ubuntu 20.10 --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8248213744..9660bb24c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM ubuntu:20.04 as build +FROM ubuntu:20.10 as build -RUN apt update && apt install -y llvm-10 clang clang-10 libclang-10-dev cmake +RUN apt update && apt install -y llvm-10 clang-10 libclang-10-dev cmake WORKDIR /lsif-clang @@ -8,7 +8,7 @@ COPY . . RUN cmake -B build && make -C build -j$(nproc) -FROM ubuntu:20.04 +FROM ubuntu:20.10 RUN apt update && apt install -y libllvm10 cmake From 2099933ad0865626d9ff1817635d778720ccac8b Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Mon, 4 Jan 2021 15:13:53 +0000 Subject: [PATCH 10/19] Added env to point cmake to clang-10 in Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9660bb24c3..672c0cc07d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ WORKDIR /lsif-clang COPY . . -RUN cmake -B build && make -C build -j$(nproc) +RUN CC=clang-10 CXX=clang-10 cmake -B build && make -C build -j$(nproc) FROM ubuntu:20.10 From 44ca8aad8c760ad5bca024efa43e9227fc4e2c2f Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Mon, 4 Jan 2021 17:53:43 +0000 Subject: [PATCH 11/19] Transition to beta stage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 94de7299fe..5a49a71160 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# lsif-clang indexer ![Status: Development](https://img.shields.io/badge/status-development-yellow?style=flat) +# lsif-clang indexer ![Status: Development](https://img.shields.io/badge/status-beta-yellow?style=flat) ![GIF displaying usage on the linux kernel.](docs/gifs/torvalds-linux.gif) From cffed4a109c1efa9e767355f5d8a8361e4347f14 Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Wed, 13 Jan 2021 15:57:26 +0000 Subject: [PATCH 12/19] Exit 1 when 0 entries in compile_commands.json/file not found --- src/indexer/IndexerMain.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/indexer/IndexerMain.cpp b/src/indexer/IndexerMain.cpp index e2bbe2cdd8..770b05b916 100644 --- a/src/indexer/IndexerMain.cpp +++ b/src/indexer/IndexerMain.cpp @@ -140,7 +140,11 @@ int main(int argc, const char **argv) { // Collect symbols found in each translation unit, merging as we go. clang::clangd::IndexFileIn Data; - AllTUsToolExecutor Executor(OptionsParser.getCompilations(), 0); + auto& compilations = OptionsParser.getCompilations(); + if (compilations.getAllFiles().size() == 0) { + exit(1); + } + AllTUsToolExecutor Executor(compilations, 0); auto Err = Executor.execute(std::make_unique(Data, ProjectRoot), Adjuster); if (Err) { From 048cebd2f13d23751bae83f95ef664769a697c5f Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Thu, 14 Jan 2021 13:33:29 +0000 Subject: [PATCH 13/19] Added DEBIAN_FRONTEND=noninteractive to Dockerfile and clang-10 to packages installed --- Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 672c0cc07d..2e49eb8719 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,10 @@ RUN CC=clang-10 CXX=clang-10 cmake -B build && make -C build -j$(nproc) FROM ubuntu:20.10 -RUN apt update && apt install -y libllvm10 cmake +RUN apt update && apt install -y libllvm10 cmake clang-10 + +# Might as well set this, for auto-index purposes +ENV DEBIAN_FRONTEND=noninteractive COPY --from=build /lsif-clang/bin/lsif-clang /usr/local/bin/lsif-clang From 55bf7ff28697123fa1265ac64e01333116b34fa6 Mon Sep 17 00:00:00 2001 From: Garo Brik Date: Wed, 3 Feb 2021 10:49:47 -0800 Subject: [PATCH 14/19] --debug-files --- src/index/LSIFSerialization.cpp | 6 +++++- src/index/Serialization.h | 1 + src/indexer/IndexerMain.cpp | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/index/LSIFSerialization.cpp b/src/index/LSIFSerialization.cpp index 57d8a0a6a1..dc15fd624d 100644 --- a/src/index/LSIFSerialization.cpp +++ b/src/index/LSIFSerialization.cpp @@ -64,10 +64,11 @@ template <> struct DenseMapInfo { namespace { struct LSIFMeta { LSIFMeta(llvm::raw_ostream &OS, const clang::clangd::IndexFileOut &O) - : OS(OS), ProjectRoot(O.ProjectRoot), Debug(O.Debug) {} + : OS(OS), ProjectRoot(O.ProjectRoot), Debug(O.Debug), DebugFiles(O.DebugFiles) {} llvm::raw_ostream &OS; const std::string ProjectRoot; const bool Debug; + const bool DebugFiles; int IDCounter = 0; llvm::StringMap DocumentIDs; llvm::DenseMap ReferenceResultIDs; @@ -76,6 +77,9 @@ struct LSIFMeta { bool contains(const char *FileURI) { bool contains = FileURI && llvm::StringRef(FileURI).startswith(ProjectRoot); + if (!contains && DebugFiles) { + llvm::errs() << "Ignoring file: " << llvm::StringRef(FileURI) << "\n"; + } return contains; } }; diff --git a/src/index/Serialization.h b/src/index/Serialization.h index 149977685a..deb5e63f1b 100644 --- a/src/index/Serialization.h +++ b/src/index/Serialization.h @@ -63,6 +63,7 @@ struct IndexFileOut { IndexFileFormat Format = IndexFileFormat::RIFF; std::string ProjectRoot = ""; bool Debug = false; + bool DebugFiles = false; const tooling::CompileCommand *Cmd = nullptr; IndexFileOut() = default; diff --git a/src/indexer/IndexerMain.cpp b/src/indexer/IndexerMain.cpp index 770b05b916..0c89341dc9 100644 --- a/src/indexer/IndexerMain.cpp +++ b/src/indexer/IndexerMain.cpp @@ -53,6 +53,9 @@ static cl::opt static cl::opt DebugArg("debug", cl::desc("Enable verbose debug output."), cl::init(false), cl::cat(LSIFClangCategory)); +static cl::opt DebugFilesArg("debug-files", cl::desc("Debug files being processed."), + cl::init(false), cl::cat(LSIFClangCategory)); + class IndexActionFactory : public FrontendActionFactory { public: IndexActionFactory(clang::clangd::IndexFileIn &Result, std::string &ProjectRoot) : Result(Result), ProjectRoot(ProjectRoot) {} @@ -154,6 +157,7 @@ int main(int argc, const char **argv) { clang::clangd::IndexFileOut Out(Data); Out.Format = clang::clangd::IndexFileFormat::LSIF; Out.Debug = DebugArg; + Out.DebugFiles = DebugFilesArg; Out.ProjectRoot = ProjectRoot; if (!IndexDestinationArg.empty()) { std::error_code FileErr; From 75e801d97ab45a4ee8ec99f2f5cdb3e42e942227 Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Thu, 19 Nov 2020 15:52:19 +0000 Subject: [PATCH 15/19] introduces backward-cpp based backtraces on segfaults --- .gitignore | 5 ++++- .gitmodules | 3 +++ CMakeLists.txt | 2 ++ README.md | 2 +- docs/examples.md | 2 +- docs/images/stacktrace.png | Bin 0 -> 56798 bytes docs/{gifs => images}/torvalds-linux.gif | Bin docs/install.md | 6 ++++-- src/indexer/CMakeLists.txt | 9 +++++++-- src/indexer/IndexerMain.cpp | 4 +++- src/indexer/backward-cpp | 1 + 11 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 .gitmodules create mode 100644 docs/images/stacktrace.png rename docs/{gifs => images}/torvalds-linux.gif (100%) create mode 160000 src/indexer/backward-cpp diff --git a/.gitignore b/.gitignore index 09479aee72..33b563f521 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ valgrind* .cache bin build -cmake-build-debug* \ No newline at end of file +cmake-build-debug* +.clangd +compile_commands.json +dump.lsif diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..4225bd1d50 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/indexer/backward-cpp"] + path = src/indexer/backward-cpp + url = git@github.com:bombela/backward-cpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 069a0aee90..407381f9ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.16) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_BUILD_TYPE RelWithDebInfo) +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -DNDEBUG -g") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) diff --git a/README.md b/README.md index 5a49a71160..686dbe3c02 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # lsif-clang indexer ![Status: Development](https://img.shields.io/badge/status-beta-yellow?style=flat) -![GIF displaying usage on the linux kernel.](docs/gifs/torvalds-linux.gif) +![GIF displaying usage on the linux kernel.](docs/images/torvalds-linux.gif) This project is a fork of [clangd](https://clangd.llvm.org/) with patches to add support for outputting [LSIF indexes](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.5.0/specification/). Specifically, a fork of the `clang-tools-extra/clangd` subdirectory of the [llvm-project repo](https://github.com/llvm/llvm-project/). diff --git a/docs/examples.md b/docs/examples.md index 6f30b04da3..e39f507b56 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -4,7 +4,7 @@ This page provides examples of generating compile_commands.json files for differ ## [github.com/torvalds/linux](https://github.com/torvalds/linux) -![GIF displaying usage on the linux kernel.](gifs/torvalds-linux.gif) +![GIF displaying usage on the linux kernel.](images/torvalds-linux.gif) Once you've installed the kernel dependencies (you can use the table in `Documentation/process/changes.rst`), run the following commands from the repository root: ```sh diff --git a/docs/images/stacktrace.png b/docs/images/stacktrace.png new file mode 100644 index 0000000000000000000000000000000000000000..ac4c056d3ead6aa1472ad809c13328dd59be359a GIT binary patch literal 56798 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 Note: lsif-clang must currently be built using LLVM 10 # Installation +Make sure to checkout any submodules, either with `git clone --recurse-submodules ...` or `git submodule update --init --recursive` + ### Ubuntu ```sh diff --git a/src/indexer/CMakeLists.txt b/src/indexer/CMakeLists.txt index 6daba144c4..3798f1583f 100644 --- a/src/indexer/CMakeLists.txt +++ b/src/indexer/CMakeLists.txt @@ -1,8 +1,13 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/backward-cpp) + add_executable(lsif-clang - IndexerMain.cpp - ) + IndexerMain.cpp + ${BACKWARD_ENABLE} +) + +add_backward(lsif-clang) install(TARGETS lsif-clang) diff --git a/src/indexer/IndexerMain.cpp b/src/indexer/IndexerMain.cpp index 0c89341dc9..c5d10b3783 100644 --- a/src/indexer/IndexerMain.cpp +++ b/src/indexer/IndexerMain.cpp @@ -32,6 +32,8 @@ #include "llvm/Support/raw_ostream.h" #include +#define BACKWARD_HAS_DWARF 1 + using namespace clang::tooling; using namespace llvm; @@ -119,7 +121,7 @@ class IndexActionFactory : public FrontendActionFactory { }; int main(int argc, const char **argv) { - sys::PrintStackTraceOnErrorSignal(argv[0]); + //sys::PrintStackTraceOnErrorSignal(argv[0]); CommonOptionsParser OptionsParser(argc, argv, LSIFClangCategory, cl::OneOrMore); diff --git a/src/indexer/backward-cpp b/src/indexer/backward-cpp new file mode 160000 index 0000000000..27a89004a8 --- /dev/null +++ b/src/indexer/backward-cpp @@ -0,0 +1 @@ +Subproject commit 27a89004a86fe2a665f041c198c7fbab7489e278 From 3d5a13c48271ae5c908c9e63305f97a1207ea046 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Thu, 4 Feb 2021 15:15:20 -0500 Subject: [PATCH 16/19] ci: Use https:// rather than ssh for clone --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 4225bd1d50..5fe1420c7f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "src/indexer/backward-cpp"] - path = src/indexer/backward-cpp - url = git@github.com:bombela/backward-cpp.git + path = src/indexer/backward-cpp + url = https://github.com/bombela/backward-cpp.git From f660a58c4205d91019fba651f51df078197ef874 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Thu, 4 Feb 2021 15:23:43 -0500 Subject: [PATCH 17/19] ci: pull_request workflow --- .github/workflows/pull_request.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/pull_request.yml diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 0000000000..d43672257e --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,25 @@ +name: pull_request + +on: [pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Unshallow + run: git fetch --prune --unshallow && git submodule update --init --recursive + - uses: docker/setup-buildx-action@v1 + - name: Docker login + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Just build (no push) + id: docker_build + uses: docker/build-push-action@v2 + with: + push: false + - name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }} From aa1bcc8b2c2268bd5214365977ecf19421fe1b36 Mon Sep 17 00:00:00 2001 From: "garo (they/them)" Date: Mon, 8 Feb 2021 18:36:26 -0800 Subject: [PATCH 18/19] escape the project root we compare paths against (#42) --- src/indexer/IndexerMain.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/indexer/IndexerMain.cpp b/src/indexer/IndexerMain.cpp index c5d10b3783..c5ddfec6df 100644 --- a/src/indexer/IndexerMain.cpp +++ b/src/indexer/IndexerMain.cpp @@ -128,12 +128,11 @@ int main(int argc, const char **argv) { std::string ProjectRoot = ProjectRootArg; if (ProjectRoot == "") { - SmallString<128> CurrentPath; + SmallString<1024> CurrentPath; sys::fs::current_path(CurrentPath); - ProjectRoot = std::string("file://") + CurrentPath.c_str(); - } else if (ProjectRoot.rfind("file://", 0) != 0) { - ProjectRoot = std::string("file://") + ProjectRoot; + ProjectRoot = std::string(CurrentPath.c_str()); } + ProjectRoot = clang::clangd::URI("file", "", ProjectRoot).toString(); if (DebugArg) { llvm::errs() << "Using project root " << ProjectRoot << "\n"; } From 935aafea0e09de96b4c866d91c0587b8b1289efa Mon Sep 17 00:00:00 2001 From: Noah Santschi-Cooney Date: Mon, 18 Jan 2021 22:32:28 +0000 Subject: [PATCH 19/19] introduce documentation to aid users in the case of a segfault --- docs/segfault.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 docs/segfault.md diff --git a/docs/segfault.md b/docs/segfault.md new file mode 100644 index 0000000000..cf284a55ae --- /dev/null +++ b/docs/segfault.md @@ -0,0 +1,48 @@ +# How to troubleshoot a segfault + +This document outlines how to configure your system to provide enough information to debug lsif-clang in the case of a segfault. + +## Information to be provided + +Along with the information gathered by reading the section below, please also provide: + +1. lsif-clang version (commit/tag/etc) +1. Output of `ldd ` +1. lsif-clang source (self-compiled/docker/prebuilt binary) + - In the case of self-compiled, it may be useful to attach the binary in the bug report +1. One or both of the below (stacktrace/core dump) + +## Stacktrace + +The lsif-clang indexer has the ability to emit a detailed stacktrace (when possible) as shown in the screenshot below if a segfault is encountered. In the case of the stacktrace not being annotated with source fragments as shown below, we may also ask for a core dump. To shorten the feedback loop, it can be beneficial to include the core dump with the original bug report. + +![stacktrace in terminal](images/stacktrace.png) + +## Core dump + +If there's no stacktrace, then the next option is to configure the OS to save a core dump on segfault. + +As the process is a bit more involved, we will outline general guidelines for coercing the system to save a core dump. + +### Linux + +1. Run `ulimit -c unlimited` +1. Check the core-dump handler + - You can find the configured handler by running `cat /proc/sys/kernel/core_pattern` + - The output can be either a path with format verbs or a path prefixed by `|` followed by format verbs + - If the latter, then the binary at that path will be invoked with the core dump and you should read the manual for the given program as to where it stores core dumps and how to proper configure it to do so + - Else if the former, assuming the aforementioned `ulimit` command was invoked, then it should save the file at that location with the filename based on the format verbs passed + - e.g `/tmp/core-%e.%p.%h.%t` vs `|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h` +1. Configure the core-dump handler (if necessary) + - This can be done by running `sysctl -w kernel.core_pattern=` as root + +### Docker + +If invoking `lsif-clang` from a docker container, the core dump settings are inherited from the host OS (or VM if Mac/Windows). +In this case, it is highly recommended to configure the core pattern to save to a file that can be copied to the host from the container. + +### Mac OS + +1. Run `ulimit -c unlimited` +1. Make sure `/cores` directory is writeable by the current user +1. And that should be it : )