1e5dd7070Spatrick //===- CIndexer.cpp - Clang-C Source Indexing Library ---------------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file implements the Clang-C Source Indexing library.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick
13e5dd7070Spatrick #include "CIndexer.h"
14e5dd7070Spatrick #include "CXString.h"
15e5dd7070Spatrick #include "clang/Basic/LLVM.h"
16e5dd7070Spatrick #include "clang/Basic/Version.h"
17e5dd7070Spatrick #include "clang/Driver/Driver.h"
18e5dd7070Spatrick #include "llvm/ADT/STLExtras.h"
19e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
20ec727ea7Spatrick #include "llvm/Support/FileSystem.h"
21e5dd7070Spatrick #include "llvm/Support/MD5.h"
22e5dd7070Spatrick #include "llvm/Support/Path.h"
23e5dd7070Spatrick #include "llvm/Support/Program.h"
24e5dd7070Spatrick #include "llvm/Support/YAMLParser.h"
25e5dd7070Spatrick #include <cstdio>
26e5dd7070Spatrick #include <mutex>
27e5dd7070Spatrick
28e5dd7070Spatrick #ifdef __CYGWIN__
29e5dd7070Spatrick #include <cygwin/version.h>
30e5dd7070Spatrick #include <sys/cygwin.h>
31e5dd7070Spatrick #define _WIN32 1
32e5dd7070Spatrick #endif
33e5dd7070Spatrick
34e5dd7070Spatrick #ifdef _WIN32
35e5dd7070Spatrick #include <windows.h>
36e5dd7070Spatrick #elif defined(_AIX)
37e5dd7070Spatrick #include <errno.h>
38e5dd7070Spatrick #include <sys/ldr.h>
39e5dd7070Spatrick #else
40e5dd7070Spatrick #include <dlfcn.h>
41e5dd7070Spatrick #endif
42e5dd7070Spatrick
43e5dd7070Spatrick using namespace clang;
44e5dd7070Spatrick
45e5dd7070Spatrick #ifdef _AIX
46e5dd7070Spatrick namespace clang {
47e5dd7070Spatrick namespace {
48e5dd7070Spatrick
49e5dd7070Spatrick template <typename LibClangPathType>
getClangResourcesPathImplAIX(LibClangPathType & LibClangPath)50e5dd7070Spatrick void getClangResourcesPathImplAIX(LibClangPathType &LibClangPath) {
51e5dd7070Spatrick int PrevErrno = errno;
52e5dd7070Spatrick
53e5dd7070Spatrick size_t BufSize = 2048u;
54e5dd7070Spatrick std::unique_ptr<char[]> Buf;
55e5dd7070Spatrick while (true) {
56e5dd7070Spatrick Buf = std::make_unique<char []>(BufSize);
57e5dd7070Spatrick errno = 0;
58e5dd7070Spatrick int Ret = loadquery(L_GETXINFO, Buf.get(), (unsigned int)BufSize);
59e5dd7070Spatrick if (Ret != -1)
60e5dd7070Spatrick break; // loadquery() was successful.
61e5dd7070Spatrick if (errno != ENOMEM)
62e5dd7070Spatrick llvm_unreachable("Encountered an unexpected loadquery() failure");
63e5dd7070Spatrick
64e5dd7070Spatrick // errno == ENOMEM; try to allocate more memory.
65e5dd7070Spatrick if ((BufSize & ~((-1u) >> 1u)) != 0u)
66e5dd7070Spatrick llvm::report_fatal_error("BufSize needed for loadquery() too large");
67e5dd7070Spatrick
68e5dd7070Spatrick Buf.release();
69e5dd7070Spatrick BufSize <<= 1u;
70e5dd7070Spatrick }
71e5dd7070Spatrick
72e5dd7070Spatrick // Extract the function entry point from the function descriptor.
73e5dd7070Spatrick uint64_t EntryAddr =
74e5dd7070Spatrick reinterpret_cast<uintptr_t &>(clang_createTranslationUnit);
75e5dd7070Spatrick
76e5dd7070Spatrick // Loop to locate the function entry point in the loadquery() results.
77e5dd7070Spatrick ld_xinfo *CurInfo = reinterpret_cast<ld_xinfo *>(Buf.get());
78e5dd7070Spatrick while (true) {
79e5dd7070Spatrick uint64_t CurTextStart = (uint64_t)CurInfo->ldinfo_textorg;
80e5dd7070Spatrick uint64_t CurTextEnd = CurTextStart + CurInfo->ldinfo_textsize;
81e5dd7070Spatrick if (CurTextStart <= EntryAddr && EntryAddr < CurTextEnd)
82e5dd7070Spatrick break; // Successfully located.
83e5dd7070Spatrick
84e5dd7070Spatrick if (CurInfo->ldinfo_next == 0u)
85e5dd7070Spatrick llvm::report_fatal_error("Cannot locate entry point in "
86e5dd7070Spatrick "the loadquery() results");
87e5dd7070Spatrick CurInfo = reinterpret_cast<ld_xinfo *>(reinterpret_cast<char *>(CurInfo) +
88e5dd7070Spatrick CurInfo->ldinfo_next);
89e5dd7070Spatrick }
90e5dd7070Spatrick
91e5dd7070Spatrick LibClangPath += reinterpret_cast<char *>(CurInfo) + CurInfo->ldinfo_filename;
92e5dd7070Spatrick errno = PrevErrno;
93e5dd7070Spatrick }
94e5dd7070Spatrick
95e5dd7070Spatrick } // end anonymous namespace
96e5dd7070Spatrick } // end namespace clang
97e5dd7070Spatrick #endif
98e5dd7070Spatrick
getClangResourcesPath()99e5dd7070Spatrick const std::string &CIndexer::getClangResourcesPath() {
100e5dd7070Spatrick // Did we already compute the path?
101e5dd7070Spatrick if (!ResourcesPath.empty())
102e5dd7070Spatrick return ResourcesPath;
103e5dd7070Spatrick
104e5dd7070Spatrick SmallString<128> LibClangPath;
105e5dd7070Spatrick
106e5dd7070Spatrick // Find the location where this library lives (libclang.dylib).
107e5dd7070Spatrick #ifdef _WIN32
108e5dd7070Spatrick MEMORY_BASIC_INFORMATION mbi;
109e5dd7070Spatrick char path[MAX_PATH];
110e5dd7070Spatrick VirtualQuery((void *)(uintptr_t)clang_createTranslationUnit, &mbi,
111e5dd7070Spatrick sizeof(mbi));
112e5dd7070Spatrick GetModuleFileNameA((HINSTANCE)mbi.AllocationBase, path, MAX_PATH);
113e5dd7070Spatrick
114e5dd7070Spatrick #ifdef __CYGWIN__
115e5dd7070Spatrick char w32path[MAX_PATH];
116e5dd7070Spatrick strcpy(w32path, path);
117e5dd7070Spatrick #if CYGWIN_VERSION_API_MAJOR > 0 || CYGWIN_VERSION_API_MINOR >= 181
118e5dd7070Spatrick cygwin_conv_path(CCP_WIN_A_TO_POSIX, w32path, path, MAX_PATH);
119e5dd7070Spatrick #else
120e5dd7070Spatrick cygwin_conv_to_full_posix_path(w32path, path);
121e5dd7070Spatrick #endif
122e5dd7070Spatrick #endif
123e5dd7070Spatrick
124e5dd7070Spatrick LibClangPath += path;
125e5dd7070Spatrick #elif defined(_AIX)
126e5dd7070Spatrick getClangResourcesPathImplAIX(LibClangPath);
127e5dd7070Spatrick #else
128e5dd7070Spatrick Dl_info info;
129*12c85518Srobert std::string Path;
130*12c85518Srobert // This silly cast below avoids a C++ warning.
131*12c85518Srobert if (dladdr((void *)(uintptr_t)clang_createTranslationUnit, &info) != 0) {
132e5dd7070Spatrick // We now have the CIndex directory, locate clang relative to it.
133e5dd7070Spatrick LibClangPath += info.dli_fname;
134*12c85518Srobert } else if (!(Path = llvm::sys::fs::getMainExecutable(nullptr, nullptr)).empty()) {
135*12c85518Srobert // If we can't get the path using dladdr, try to get the main executable
136*12c85518Srobert // path. This may be needed when we're statically linking libclang with
137*12c85518Srobert // musl libc, for example.
138*12c85518Srobert LibClangPath += Path;
139*12c85518Srobert } else {
140*12c85518Srobert // It's rather unlikely we end up here. But it could happen, so report an
141*12c85518Srobert // error instead of crashing.
142*12c85518Srobert llvm::report_fatal_error("could not locate Clang resource path");
143*12c85518Srobert }
144*12c85518Srobert
145e5dd7070Spatrick #endif
146e5dd7070Spatrick
147e5dd7070Spatrick // Cache our result.
148e5dd7070Spatrick ResourcesPath = driver::Driver::GetResourcesPath(LibClangPath);
149e5dd7070Spatrick return ResourcesPath;
150e5dd7070Spatrick }
151e5dd7070Spatrick
getClangToolchainPath()152e5dd7070Spatrick StringRef CIndexer::getClangToolchainPath() {
153e5dd7070Spatrick if (!ToolchainPath.empty())
154e5dd7070Spatrick return ToolchainPath;
155e5dd7070Spatrick StringRef ResourcePath = getClangResourcesPath();
156ec727ea7Spatrick ToolchainPath =
157ec727ea7Spatrick std::string(llvm::sys::path::parent_path(llvm::sys::path::parent_path(
158ec727ea7Spatrick llvm::sys::path::parent_path(ResourcePath))));
159e5dd7070Spatrick return ToolchainPath;
160e5dd7070Spatrick }
161e5dd7070Spatrick
LibclangInvocationReporter(CIndexer & Idx,OperationKind Op,unsigned ParseOptions,llvm::ArrayRef<const char * > Args,llvm::ArrayRef<std::string> InvocationArgs,llvm::ArrayRef<CXUnsavedFile> UnsavedFiles)162e5dd7070Spatrick LibclangInvocationReporter::LibclangInvocationReporter(
163e5dd7070Spatrick CIndexer &Idx, OperationKind Op, unsigned ParseOptions,
164e5dd7070Spatrick llvm::ArrayRef<const char *> Args,
165e5dd7070Spatrick llvm::ArrayRef<std::string> InvocationArgs,
166e5dd7070Spatrick llvm::ArrayRef<CXUnsavedFile> UnsavedFiles) {
167e5dd7070Spatrick StringRef Path = Idx.getInvocationEmissionPath();
168e5dd7070Spatrick if (Path.empty())
169e5dd7070Spatrick return;
170e5dd7070Spatrick
171e5dd7070Spatrick // Create a temporary file for the invocation log.
172e5dd7070Spatrick SmallString<256> TempPath;
173e5dd7070Spatrick TempPath = Path;
174e5dd7070Spatrick llvm::sys::path::append(TempPath, "libclang-%%%%%%%%%%%%");
175e5dd7070Spatrick int FD;
176a9ac8606Spatrick if (llvm::sys::fs::createUniqueFile(TempPath, FD, TempPath,
177a9ac8606Spatrick llvm::sys::fs::OF_Text))
178e5dd7070Spatrick return;
179*12c85518Srobert File = static_cast<std::string>(TempPath);
180e5dd7070Spatrick llvm::raw_fd_ostream OS(FD, /*ShouldClose=*/true);
181e5dd7070Spatrick
182e5dd7070Spatrick // Write out the information about the invocation to it.
183e5dd7070Spatrick auto WriteStringKey = [&OS](StringRef Key, StringRef Value) {
184e5dd7070Spatrick OS << R"(")" << Key << R"(":")";
185e5dd7070Spatrick OS << llvm::yaml::escape(Value) << '"';
186e5dd7070Spatrick };
187e5dd7070Spatrick OS << '{';
188e5dd7070Spatrick WriteStringKey("toolchain", Idx.getClangToolchainPath());
189e5dd7070Spatrick OS << ',';
190e5dd7070Spatrick WriteStringKey("libclang.operation",
191e5dd7070Spatrick Op == OperationKind::ParseOperation ? "parse" : "complete");
192e5dd7070Spatrick OS << ',';
193e5dd7070Spatrick OS << R"("libclang.opts":)" << ParseOptions;
194e5dd7070Spatrick OS << ',';
195e5dd7070Spatrick OS << R"("args":[)";
196e5dd7070Spatrick for (const auto &I : llvm::enumerate(Args)) {
197e5dd7070Spatrick if (I.index())
198e5dd7070Spatrick OS << ',';
199e5dd7070Spatrick OS << '"' << llvm::yaml::escape(I.value()) << '"';
200e5dd7070Spatrick }
201e5dd7070Spatrick if (!InvocationArgs.empty()) {
202e5dd7070Spatrick OS << R"(],"invocation-args":[)";
203e5dd7070Spatrick for (const auto &I : llvm::enumerate(InvocationArgs)) {
204e5dd7070Spatrick if (I.index())
205e5dd7070Spatrick OS << ',';
206e5dd7070Spatrick OS << '"' << llvm::yaml::escape(I.value()) << '"';
207e5dd7070Spatrick }
208e5dd7070Spatrick }
209e5dd7070Spatrick if (!UnsavedFiles.empty()) {
210e5dd7070Spatrick OS << R"(],"unsaved_file_hashes":[)";
211e5dd7070Spatrick for (const auto &UF : llvm::enumerate(UnsavedFiles)) {
212e5dd7070Spatrick if (UF.index())
213e5dd7070Spatrick OS << ',';
214e5dd7070Spatrick OS << '{';
215e5dd7070Spatrick WriteStringKey("name", UF.value().Filename);
216e5dd7070Spatrick OS << ',';
217e5dd7070Spatrick llvm::MD5 Hash;
218e5dd7070Spatrick Hash.update(getContents(UF.value()));
219e5dd7070Spatrick llvm::MD5::MD5Result Result;
220e5dd7070Spatrick Hash.final(Result);
221e5dd7070Spatrick SmallString<32> Digest = Result.digest();
222e5dd7070Spatrick WriteStringKey("md5", Digest);
223e5dd7070Spatrick OS << '}';
224e5dd7070Spatrick }
225e5dd7070Spatrick }
226e5dd7070Spatrick OS << "]}";
227e5dd7070Spatrick }
228e5dd7070Spatrick
229e5dd7070Spatrick LibclangInvocationReporter::~LibclangInvocationReporter() {
230e5dd7070Spatrick if (!File.empty())
231e5dd7070Spatrick llvm::sys::fs::remove(File);
232e5dd7070Spatrick }
233