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