xref: /openbsd-src/gnu/llvm/clang/tools/libclang/CIndexer.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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