mirror of
				https://github.com/MaskRay/ccls.git
				synced 2025-11-03 22:04:24 +00:00 
			
		
		
		
	Use llvm::cl and simplify main.cc import_pipeline.cc
This commit is contained in:
		
							parent
							
								
									4207c3ece1
								
							
						
					
					
						commit
						c81ca26a2e
					
				@ -34,6 +34,9 @@ if(NOT CYGWIN)
 | 
			
		||||
  set_property(TARGET ccls PROPERTY CXX_EXTENSIONS OFF)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# To link against LLVM libraries (usually compiled with -fno-rtti)
 | 
			
		||||
target_compile_options(ccls PRIVATE -fno-rtti)
 | 
			
		||||
 | 
			
		||||
# CMake sets MSVC for both MSVC and Clang(Windows)
 | 
			
		||||
if(MSVC)
 | 
			
		||||
  # Common MSVC/Clang(Windows) options
 | 
			
		||||
@ -205,12 +208,12 @@ target_sources(ccls PRIVATE
 | 
			
		||||
               src/clang_indexer.cc
 | 
			
		||||
               src/clang_translation_unit.cc
 | 
			
		||||
               src/clang_utils.cc
 | 
			
		||||
               src/command_line.cc
 | 
			
		||||
               src/config.cc
 | 
			
		||||
               src/diagnostics_engine.cc
 | 
			
		||||
               src/file_consumer.cc
 | 
			
		||||
               src/filesystem.cc
 | 
			
		||||
               src/fuzzy_match.cc
 | 
			
		||||
               src/main.cc
 | 
			
		||||
               src/import_pipeline.cc
 | 
			
		||||
               src/include_complete.cc
 | 
			
		||||
               src/method.cc
 | 
			
		||||
 | 
			
		||||
@ -1,396 +0,0 @@
 | 
			
		||||
// TODO: cleanup includes
 | 
			
		||||
#include "cache_manager.h"
 | 
			
		||||
#include "clang_complete.h"
 | 
			
		||||
#include "diagnostics_engine.h"
 | 
			
		||||
#include "file_consumer.h"
 | 
			
		||||
#include "import_pipeline.h"
 | 
			
		||||
#include "include_complete.h"
 | 
			
		||||
#include "indexer.h"
 | 
			
		||||
#include "lex_utils.h"
 | 
			
		||||
#include "lru_cache.h"
 | 
			
		||||
#include "lsp_diagnostic.h"
 | 
			
		||||
#include "match.h"
 | 
			
		||||
#include "message_handler.h"
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include "project.h"
 | 
			
		||||
#include "query.h"
 | 
			
		||||
#include "query_utils.h"
 | 
			
		||||
#include "queue_manager.h"
 | 
			
		||||
#include "serializer.h"
 | 
			
		||||
#include "serializers/json.h"
 | 
			
		||||
#include "test.h"
 | 
			
		||||
#include "timer.h"
 | 
			
		||||
#include "working_files.h"
 | 
			
		||||
 | 
			
		||||
#include <doctest/doctest.h>
 | 
			
		||||
#include <rapidjson/error/en.h>
 | 
			
		||||
#include <loguru.hpp>
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
std::string g_init_options;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
std::unordered_map<std::string, std::string> ParseOptions(int argc,
 | 
			
		||||
                                                          char** argv) {
 | 
			
		||||
  std::unordered_map<std::string, std::string> output;
 | 
			
		||||
 | 
			
		||||
  for (int i = 1; i < argc; ++i) {
 | 
			
		||||
    std::string arg = argv[i];
 | 
			
		||||
    if (arg[0] == '-') {
 | 
			
		||||
      auto equal = arg.find('=');
 | 
			
		||||
      if (equal != std::string::npos) {
 | 
			
		||||
        output[arg.substr(0, equal)] = arg.substr(equal + 1);
 | 
			
		||||
      } else if (i + 1 < argc && argv[i + 1][0] != '-')
 | 
			
		||||
        output[arg] = argv[++i];
 | 
			
		||||
      else
 | 
			
		||||
        output[arg] = "";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return output;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool HasOption(const std::unordered_map<std::string, std::string>& options,
 | 
			
		||||
               const std::string& option) {
 | 
			
		||||
  return options.find(option) != options.end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function returns true if e2e timing should be displayed for the given
 | 
			
		||||
// MethodId.
 | 
			
		||||
bool ShouldDisplayMethodTiming(MethodType type) {
 | 
			
		||||
  return
 | 
			
		||||
    type != kMethodType_TextDocumentPublishDiagnostics &&
 | 
			
		||||
    type != kMethodType_CclsPublishInactiveRegions &&
 | 
			
		||||
    type != kMethodType_Unknown;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PrintHelp() {
 | 
			
		||||
  printf("%s", R"help(ccls is a C/C++/Objective-C language server.
 | 
			
		||||
 | 
			
		||||
Mode:
 | 
			
		||||
  --test-unit   Run unit tests.
 | 
			
		||||
  --test-index <opt_filter_path>
 | 
			
		||||
                Run index tests. opt_filter_path can be used to specify which
 | 
			
		||||
                test to run (ie, "foo" will run all tests which contain "foo"
 | 
			
		||||
                in the path). If not provided all tests are run.
 | 
			
		||||
  (default if no other mode is specified)
 | 
			
		||||
                Run as a language server over stdin and stdout
 | 
			
		||||
 | 
			
		||||
Other command line options:
 | 
			
		||||
  --init <initializationOptions>
 | 
			
		||||
                Override client provided initialization options
 | 
			
		||||
                https://github.com/MaskRay/ccls/wiki/Initialization-options
 | 
			
		||||
  --log-file <path>    Logging file for diagnostics
 | 
			
		||||
  --log-file-append <path>    Like --log-file, but appending
 | 
			
		||||
  --log-all-to-stderr  Write all log messages to STDERR.
 | 
			
		||||
  --help        Print this help information.
 | 
			
		||||
  --ci          Prevents tests from prompting the user for input. Used for
 | 
			
		||||
                continuous integration so it can fail faster instead of timing
 | 
			
		||||
                out.
 | 
			
		||||
 | 
			
		||||
See more on https://github.com/MaskRay/ccls/wiki
 | 
			
		||||
)help");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// QUERYDB MAIN ////////////////////////////////////////////////////////////////
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
bool QueryDbMainLoop(QueryDatabase* db,
 | 
			
		||||
                     MultiQueueWaiter* waiter,
 | 
			
		||||
                     Project* project,
 | 
			
		||||
                     VFS* vfs,
 | 
			
		||||
                     ImportPipelineStatus* status,
 | 
			
		||||
                     SemanticHighlightSymbolCache* semantic_cache,
 | 
			
		||||
                     WorkingFiles* working_files,
 | 
			
		||||
                     ClangCompleteManager* clang_complete,
 | 
			
		||||
                     IncludeComplete* include_complete,
 | 
			
		||||
                     CodeCompleteCache* global_code_complete_cache,
 | 
			
		||||
                     CodeCompleteCache* non_global_code_complete_cache,
 | 
			
		||||
                     CodeCompleteCache* signature_cache) {
 | 
			
		||||
  auto* queue = QueueManager::instance();
 | 
			
		||||
  std::vector<std::unique_ptr<InMessage>> messages =
 | 
			
		||||
      queue->for_querydb.DequeueAll();
 | 
			
		||||
  bool did_work = messages.size();
 | 
			
		||||
  for (auto& message : messages) {
 | 
			
		||||
    // TODO: Consider using std::unordered_map to lookup the handler
 | 
			
		||||
    for (MessageHandler* handler : *MessageHandler::message_handlers) {
 | 
			
		||||
      if (handler->GetMethodType() == message->GetMethodType()) {
 | 
			
		||||
        handler->Run(std::move(message));
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (message) {
 | 
			
		||||
      LOG_S(ERROR) << "No handler for " << message->GetMethodType();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // TODO: consider rate-limiting and checking for IPC messages so we don't
 | 
			
		||||
  // block requests / we can serve partial requests.
 | 
			
		||||
 | 
			
		||||
  if (QueryDb_ImportMain(db, status, semantic_cache, working_files)) {
 | 
			
		||||
    did_work = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return did_work;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RunQueryDbThread(const std::string& bin_name,
 | 
			
		||||
                      MultiQueueWaiter* querydb_waiter,
 | 
			
		||||
                      MultiQueueWaiter* indexer_waiter) {
 | 
			
		||||
  Project project;
 | 
			
		||||
  SemanticHighlightSymbolCache semantic_cache;
 | 
			
		||||
  WorkingFiles working_files;
 | 
			
		||||
  VFS vfs;
 | 
			
		||||
  DiagnosticsEngine diag_engine;
 | 
			
		||||
 | 
			
		||||
  ClangCompleteManager clang_complete(
 | 
			
		||||
      &project, &working_files,
 | 
			
		||||
      [&](std::string path, std::vector<lsDiagnostic> diagnostics) {
 | 
			
		||||
        diag_engine.Publish(&working_files, path, diagnostics);
 | 
			
		||||
      },
 | 
			
		||||
      [](lsRequestId id) {
 | 
			
		||||
        if (id.Valid()) {
 | 
			
		||||
          Out_Error out;
 | 
			
		||||
          out.id = id;
 | 
			
		||||
          out.error.code = lsErrorCodes::InternalError;
 | 
			
		||||
          out.error.message =
 | 
			
		||||
              "Dropping completion request; a newer request "
 | 
			
		||||
              "has come in that will be serviced instead.";
 | 
			
		||||
          QueueManager::WriteStdout(kMethodType_Unknown, out);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
  IncludeComplete include_complete(&project);
 | 
			
		||||
  auto global_code_complete_cache = std::make_unique<CodeCompleteCache>();
 | 
			
		||||
  auto non_global_code_complete_cache = std::make_unique<CodeCompleteCache>();
 | 
			
		||||
  auto signature_cache = std::make_unique<CodeCompleteCache>();
 | 
			
		||||
  ImportPipelineStatus import_pipeline_status;
 | 
			
		||||
  QueryDatabase db;
 | 
			
		||||
 | 
			
		||||
  // Setup shared references.
 | 
			
		||||
  for (MessageHandler* handler : *MessageHandler::message_handlers) {
 | 
			
		||||
    handler->db = &db;
 | 
			
		||||
    handler->waiter = indexer_waiter;
 | 
			
		||||
    handler->project = &project;
 | 
			
		||||
    handler->diag_engine = &diag_engine;
 | 
			
		||||
    handler->vfs = &vfs;
 | 
			
		||||
    handler->import_pipeline_status = &import_pipeline_status;
 | 
			
		||||
    handler->semantic_cache = &semantic_cache;
 | 
			
		||||
    handler->working_files = &working_files;
 | 
			
		||||
    handler->clang_complete = &clang_complete;
 | 
			
		||||
    handler->include_complete = &include_complete;
 | 
			
		||||
    handler->global_code_complete_cache = global_code_complete_cache.get();
 | 
			
		||||
    handler->non_global_code_complete_cache =
 | 
			
		||||
        non_global_code_complete_cache.get();
 | 
			
		||||
    handler->signature_cache = signature_cache.get();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Run query db main loop.
 | 
			
		||||
  SetThreadName("querydb");
 | 
			
		||||
  while (true) {
 | 
			
		||||
    bool did_work = QueryDbMainLoop(
 | 
			
		||||
        &db, querydb_waiter, &project, &vfs,
 | 
			
		||||
        &import_pipeline_status,
 | 
			
		||||
        &semantic_cache, &working_files, &clang_complete, &include_complete,
 | 
			
		||||
        global_code_complete_cache.get(), non_global_code_complete_cache.get(),
 | 
			
		||||
        signature_cache.get());
 | 
			
		||||
 | 
			
		||||
    // Cleanup and free any unused memory.
 | 
			
		||||
    FreeUnusedMemory();
 | 
			
		||||
 | 
			
		||||
    if (!did_work) {
 | 
			
		||||
      auto* queue = QueueManager::instance();
 | 
			
		||||
      querydb_waiter->Wait(&queue->on_indexed, &queue->for_querydb);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// STDIN MAIN //////////////////////////////////////////////////////////////////
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Separate thread whose only job is to read from stdin and
 | 
			
		||||
// dispatch read commands to the actual indexer program. This
 | 
			
		||||
// cannot be done on the main thread because reading from std::cin
 | 
			
		||||
// blocks.
 | 
			
		||||
//
 | 
			
		||||
// |ipc| is connected to a server.
 | 
			
		||||
void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
 | 
			
		||||
  std::thread([request_times]() {
 | 
			
		||||
    SetThreadName("stdin");
 | 
			
		||||
    auto* queue = QueueManager::instance();
 | 
			
		||||
    while (true) {
 | 
			
		||||
      std::unique_ptr<InMessage> message;
 | 
			
		||||
      std::optional<std::string> err =
 | 
			
		||||
          MessageRegistry::instance()->ReadMessageFromStdin(&message);
 | 
			
		||||
 | 
			
		||||
      // Message parsing can fail if we don't recognize the method.
 | 
			
		||||
      if (err) {
 | 
			
		||||
        // The message may be partially deserialized.
 | 
			
		||||
        // Emit an error ResponseMessage if |id| is available.
 | 
			
		||||
        if (message) {
 | 
			
		||||
          lsRequestId id = message->GetRequestId();
 | 
			
		||||
          if (id.Valid()) {
 | 
			
		||||
            Out_Error out;
 | 
			
		||||
            out.id = id;
 | 
			
		||||
            out.error.code = lsErrorCodes::InvalidParams;
 | 
			
		||||
            out.error.message = std::move(*err);
 | 
			
		||||
            queue->WriteStdout(kMethodType_Unknown, out);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Cache |method_id| so we can access it after moving |message|.
 | 
			
		||||
      MethodType method_type = message->GetMethodType();
 | 
			
		||||
      (*request_times)[method_type] = Timer();
 | 
			
		||||
 | 
			
		||||
      queue->for_querydb.PushBack(std::move(message));
 | 
			
		||||
 | 
			
		||||
      // If the message was to exit then querydb will take care of the actual
 | 
			
		||||
      // exit. Stop reading from stdin since it might be detached.
 | 
			
		||||
      if (method_type == kMethodType_Exit)
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }).detach();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
 | 
			
		||||
                        MultiQueueWaiter* waiter) {
 | 
			
		||||
  std::thread([=]() {
 | 
			
		||||
    SetThreadName("stdout");
 | 
			
		||||
    auto* queue = QueueManager::instance();
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
      std::vector<Stdout_Request> messages = queue->for_stdout.DequeueAll();
 | 
			
		||||
      if (messages.empty()) {
 | 
			
		||||
        waiter->Wait(&queue->for_stdout);
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      for (auto& message : messages) {
 | 
			
		||||
        if (ShouldDisplayMethodTiming(message.method)) {
 | 
			
		||||
          Timer time = (*request_times)[message.method];
 | 
			
		||||
          time.ResetAndPrint("[e2e] Running " + std::string(message.method));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fwrite(message.content.c_str(), message.content.size(), 1, stdout);
 | 
			
		||||
        fflush(stdout);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }).detach();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LanguageServerMain(const std::string& bin_name,
 | 
			
		||||
                        MultiQueueWaiter* querydb_waiter,
 | 
			
		||||
                        MultiQueueWaiter* indexer_waiter,
 | 
			
		||||
                        MultiQueueWaiter* stdout_waiter) {
 | 
			
		||||
  std::unordered_map<MethodType, Timer> request_times;
 | 
			
		||||
 | 
			
		||||
  LaunchStdinLoop(&request_times);
 | 
			
		||||
 | 
			
		||||
  // We run a dedicated thread for writing to stdout because there can be an
 | 
			
		||||
  // unknown number of delays when output information.
 | 
			
		||||
  LaunchStdoutThread(&request_times, stdout_waiter);
 | 
			
		||||
 | 
			
		||||
  // Start querydb which takes over this thread. The querydb will launch
 | 
			
		||||
  // indexer threads as needed.
 | 
			
		||||
  RunQueryDbThread(bin_name, querydb_waiter, indexer_waiter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv) {
 | 
			
		||||
  TraceMe();
 | 
			
		||||
 | 
			
		||||
  std::unordered_map<std::string, std::string> options =
 | 
			
		||||
      ParseOptions(argc, argv);
 | 
			
		||||
 | 
			
		||||
  if (HasOption(options, "-h") || HasOption(options, "--help")) {
 | 
			
		||||
    PrintHelp();
 | 
			
		||||
    // Also emit doctest help if --test-unit is passed.
 | 
			
		||||
    if (!HasOption(options, "--test-unit"))
 | 
			
		||||
      return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!HasOption(options, "--log-all-to-stderr"))
 | 
			
		||||
    loguru::g_stderr_verbosity = loguru::Verbosity_WARNING;
 | 
			
		||||
 | 
			
		||||
  loguru::g_preamble_date = false;
 | 
			
		||||
  loguru::g_preamble_time = false;
 | 
			
		||||
  loguru::g_preamble_verbose = false;
 | 
			
		||||
  loguru::g_flush_interval_ms = 0;
 | 
			
		||||
  loguru::init(argc, argv);
 | 
			
		||||
 | 
			
		||||
  MultiQueueWaiter querydb_waiter, indexer_waiter, stdout_waiter;
 | 
			
		||||
  QueueManager::Init(&querydb_waiter, &indexer_waiter, &stdout_waiter);
 | 
			
		||||
 | 
			
		||||
  PlatformInit();
 | 
			
		||||
  IndexInit();
 | 
			
		||||
 | 
			
		||||
  bool language_server = true;
 | 
			
		||||
 | 
			
		||||
  if (HasOption(options, "--log-file")) {
 | 
			
		||||
    loguru::add_file(options["--log-file"].c_str(), loguru::Truncate,
 | 
			
		||||
                     loguru::Verbosity_MAX);
 | 
			
		||||
  }
 | 
			
		||||
  if (HasOption(options, "--log-file-append")) {
 | 
			
		||||
    loguru::add_file(options["--log-file-append"].c_str(), loguru::Append,
 | 
			
		||||
                     loguru::Verbosity_MAX);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (HasOption(options, "--test-unit")) {
 | 
			
		||||
    language_server = false;
 | 
			
		||||
    doctest::Context context;
 | 
			
		||||
    context.applyCommandLine(argc, argv);
 | 
			
		||||
    int res = context.run();
 | 
			
		||||
    if (res != 0 || context.shouldExit())
 | 
			
		||||
      return res;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (HasOption(options, "--test-index")) {
 | 
			
		||||
    language_server = false;
 | 
			
		||||
    if (!RunIndexTests(options["--test-index"], !HasOption(options, "--ci")))
 | 
			
		||||
      return 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (language_server) {
 | 
			
		||||
    if (HasOption(options, "--init")) {
 | 
			
		||||
      // We check syntax error here but override client-side
 | 
			
		||||
      // initializationOptions in messages/initialize.cc
 | 
			
		||||
      g_init_options = options["--init"];
 | 
			
		||||
      rapidjson::Document reader;
 | 
			
		||||
      rapidjson::ParseResult ok = reader.Parse(g_init_options.c_str());
 | 
			
		||||
      if (!ok) {
 | 
			
		||||
        fprintf(stderr, "Failed to parse --init as JSON: %s (%zd)\n",
 | 
			
		||||
                rapidjson::GetParseError_En(ok.Code()), ok.Offset());
 | 
			
		||||
        return 1;
 | 
			
		||||
      }
 | 
			
		||||
      JsonReader json_reader{&reader};
 | 
			
		||||
      try {
 | 
			
		||||
        Config config;
 | 
			
		||||
        Reflect(json_reader, config);
 | 
			
		||||
      } catch (std::invalid_argument& e) {
 | 
			
		||||
        fprintf(stderr, "Failed to parse --init %s, expected %s\n",
 | 
			
		||||
                static_cast<JsonReader&>(json_reader).GetPath().c_str(),
 | 
			
		||||
                e.what());
 | 
			
		||||
        return 1;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LanguageServerMain(argv[0], &querydb_waiter, &indexer_waiter,
 | 
			
		||||
                       &stdout_waiter);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "position.h"
 | 
			
		||||
#include "serializer.h"
 | 
			
		||||
#include "utils.h"
 | 
			
		||||
 | 
			
		||||
#include <clang-c/Index.h>
 | 
			
		||||
@ -77,3 +78,30 @@ struct FileConsumer {
 | 
			
		||||
  std::string parse_file_;
 | 
			
		||||
  int thread_id_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Contains timing information for the entire pipeline for importing a file
 | 
			
		||||
// into the querydb.
 | 
			
		||||
struct PerformanceImportFile {
 | 
			
		||||
  // All units are in microseconds.
 | 
			
		||||
 | 
			
		||||
  // [indexer] clang parsing the file
 | 
			
		||||
  uint64_t index_parse = 0;
 | 
			
		||||
  // [indexer] build the IndexFile object from clang parse
 | 
			
		||||
  uint64_t index_build = 0;
 | 
			
		||||
  // [indexer] save the IndexFile to disk
 | 
			
		||||
  uint64_t index_save_to_disk = 0;
 | 
			
		||||
  // [indexer] loading previously cached index
 | 
			
		||||
  uint64_t index_load_cached = 0;
 | 
			
		||||
  // [indexer] create delta IndexUpdate object
 | 
			
		||||
  uint64_t index_make_delta = 0;
 | 
			
		||||
  // [querydb] update WorkingFile indexed file state
 | 
			
		||||
  // uint64_t querydb_update_working_file = 0;
 | 
			
		||||
  // [querydb] apply IndexUpdate
 | 
			
		||||
  // uint64_t querydb_apply_index_update = 0;
 | 
			
		||||
};
 | 
			
		||||
MAKE_REFLECT_STRUCT(PerformanceImportFile,
 | 
			
		||||
                    index_parse,
 | 
			
		||||
                    index_build,
 | 
			
		||||
                    index_save_to_disk,
 | 
			
		||||
                    index_load_cached,
 | 
			
		||||
                    index_make_delta);
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
#include "import_pipeline.h"
 | 
			
		||||
 | 
			
		||||
#include "cache_manager.h"
 | 
			
		||||
#include "clang_complete.h"
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "diagnostics_engine.h"
 | 
			
		||||
#include "include_complete.h"
 | 
			
		||||
#include "lsp.h"
 | 
			
		||||
#include "message_handler.h"
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
@ -15,6 +17,7 @@
 | 
			
		||||
#include <loguru.hpp>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
@ -201,11 +204,19 @@ bool Indexer_Parse(DiagnosticsEngine* diag_engine,
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function returns true if e2e timing should be displayed for the given
 | 
			
		||||
// MethodId.
 | 
			
		||||
bool ShouldDisplayMethodTiming(MethodType type) {
 | 
			
		||||
  return
 | 
			
		||||
    type != kMethodType_TextDocumentPublishDiagnostics &&
 | 
			
		||||
    type != kMethodType_CclsPublishInactiveRegions &&
 | 
			
		||||
    type != kMethodType_Unknown;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
void Indexer_Main(DiagnosticsEngine* diag_engine,
 | 
			
		||||
                  VFS* vfs,
 | 
			
		||||
                  ImportPipelineStatus* status,
 | 
			
		||||
                  Project* project,
 | 
			
		||||
                  WorkingFiles* working_files,
 | 
			
		||||
                  MultiQueueWaiter* waiter) {
 | 
			
		||||
@ -218,10 +229,8 @@ void Indexer_Main(DiagnosticsEngine* diag_engine,
 | 
			
		||||
      waiter->Wait(&queue->index_request);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
void QueryDb_OnIndexed(QueueManager* queue,
 | 
			
		||||
                       QueryDatabase* db,
 | 
			
		||||
                       ImportPipelineStatus* status,
 | 
			
		||||
                       SemanticHighlightSymbolCache* semantic_cache,
 | 
			
		||||
                       WorkingFiles* working_files,
 | 
			
		||||
                       Index_OnIndexed* response) {
 | 
			
		||||
@ -264,23 +273,154 @@ void QueryDb_OnIndexed(QueueManager* queue,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
 | 
			
		||||
  std::thread([request_times]() {
 | 
			
		||||
    SetThreadName("stdin");
 | 
			
		||||
    auto* queue = QueueManager::instance();
 | 
			
		||||
    while (true) {
 | 
			
		||||
      std::unique_ptr<InMessage> message;
 | 
			
		||||
      std::optional<std::string> err =
 | 
			
		||||
          MessageRegistry::instance()->ReadMessageFromStdin(&message);
 | 
			
		||||
 | 
			
		||||
bool QueryDb_ImportMain(QueryDatabase* db,
 | 
			
		||||
                        ImportPipelineStatus* status,
 | 
			
		||||
                        SemanticHighlightSymbolCache* semantic_cache,
 | 
			
		||||
                        WorkingFiles* working_files) {
 | 
			
		||||
  auto* queue = QueueManager::instance();
 | 
			
		||||
  bool did_work = false;
 | 
			
		||||
      // Message parsing can fail if we don't recognize the method.
 | 
			
		||||
      if (err) {
 | 
			
		||||
        // The message may be partially deserialized.
 | 
			
		||||
        // Emit an error ResponseMessage if |id| is available.
 | 
			
		||||
        if (message) {
 | 
			
		||||
          lsRequestId id = message->GetRequestId();
 | 
			
		||||
          if (id.Valid()) {
 | 
			
		||||
            Out_Error out;
 | 
			
		||||
            out.id = id;
 | 
			
		||||
            out.error.code = lsErrorCodes::InvalidParams;
 | 
			
		||||
            out.error.message = std::move(*err);
 | 
			
		||||
            queue->WriteStdout(kMethodType_Unknown, out);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
  for (int i = 80; i--; ) {
 | 
			
		||||
    std::optional<Index_OnIndexed> response = queue->on_indexed.TryPopFront();
 | 
			
		||||
    if (!response)
 | 
			
		||||
      break;
 | 
			
		||||
    did_work = true;
 | 
			
		||||
    QueryDb_OnIndexed(queue, db, status, semantic_cache, working_files,
 | 
			
		||||
                      &*response);
 | 
			
		||||
      // Cache |method_id| so we can access it after moving |message|.
 | 
			
		||||
      MethodType method_type = message->GetMethodType();
 | 
			
		||||
      (*request_times)[method_type] = Timer();
 | 
			
		||||
 | 
			
		||||
      queue->for_querydb.PushBack(std::move(message));
 | 
			
		||||
 | 
			
		||||
      // If the message was to exit then querydb will take care of the actual
 | 
			
		||||
      // exit. Stop reading from stdin since it might be detached.
 | 
			
		||||
      if (method_type == kMethodType_Exit)
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }).detach();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
 | 
			
		||||
                        MultiQueueWaiter* waiter) {
 | 
			
		||||
  std::thread([=]() {
 | 
			
		||||
    SetThreadName("stdout");
 | 
			
		||||
    auto* queue = QueueManager::instance();
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
      std::vector<Stdout_Request> messages = queue->for_stdout.DequeueAll();
 | 
			
		||||
      if (messages.empty()) {
 | 
			
		||||
        waiter->Wait(&queue->for_stdout);
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      for (auto& message : messages) {
 | 
			
		||||
        if (ShouldDisplayMethodTiming(message.method)) {
 | 
			
		||||
          Timer time = (*request_times)[message.method];
 | 
			
		||||
          time.ResetAndPrint("[e2e] Running " + std::string(message.method));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fwrite(message.content.c_str(), message.content.size(), 1, stdout);
 | 
			
		||||
        fflush(stdout);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }).detach();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainLoop(MultiQueueWaiter* querydb_waiter,
 | 
			
		||||
              MultiQueueWaiter* indexer_waiter) {
 | 
			
		||||
  Project project;
 | 
			
		||||
  SemanticHighlightSymbolCache semantic_cache;
 | 
			
		||||
  WorkingFiles working_files;
 | 
			
		||||
  VFS vfs;
 | 
			
		||||
  DiagnosticsEngine diag_engine;
 | 
			
		||||
 | 
			
		||||
  ClangCompleteManager clang_complete(
 | 
			
		||||
      &project, &working_files,
 | 
			
		||||
      [&](std::string path, std::vector<lsDiagnostic> diagnostics) {
 | 
			
		||||
        diag_engine.Publish(&working_files, path, diagnostics);
 | 
			
		||||
      },
 | 
			
		||||
      [](lsRequestId id) {
 | 
			
		||||
        if (id.Valid()) {
 | 
			
		||||
          Out_Error out;
 | 
			
		||||
          out.id = id;
 | 
			
		||||
          out.error.code = lsErrorCodes::InternalError;
 | 
			
		||||
          out.error.message =
 | 
			
		||||
              "Dropping completion request; a newer request "
 | 
			
		||||
              "has come in that will be serviced instead.";
 | 
			
		||||
          QueueManager::WriteStdout(kMethodType_Unknown, out);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
  IncludeComplete include_complete(&project);
 | 
			
		||||
  auto global_code_complete_cache = std::make_unique<CodeCompleteCache>();
 | 
			
		||||
  auto non_global_code_complete_cache = std::make_unique<CodeCompleteCache>();
 | 
			
		||||
  auto signature_cache = std::make_unique<CodeCompleteCache>();
 | 
			
		||||
  QueryDatabase db;
 | 
			
		||||
 | 
			
		||||
  // Setup shared references.
 | 
			
		||||
  for (MessageHandler* handler : *MessageHandler::message_handlers) {
 | 
			
		||||
    handler->db = &db;
 | 
			
		||||
    handler->waiter = indexer_waiter;
 | 
			
		||||
    handler->project = &project;
 | 
			
		||||
    handler->diag_engine = &diag_engine;
 | 
			
		||||
    handler->vfs = &vfs;
 | 
			
		||||
    handler->semantic_cache = &semantic_cache;
 | 
			
		||||
    handler->working_files = &working_files;
 | 
			
		||||
    handler->clang_complete = &clang_complete;
 | 
			
		||||
    handler->include_complete = &include_complete;
 | 
			
		||||
    handler->global_code_complete_cache = global_code_complete_cache.get();
 | 
			
		||||
    handler->non_global_code_complete_cache =
 | 
			
		||||
        non_global_code_complete_cache.get();
 | 
			
		||||
    handler->signature_cache = signature_cache.get();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return did_work;
 | 
			
		||||
  // Run query db main loop.
 | 
			
		||||
  SetThreadName("querydb");
 | 
			
		||||
  auto* queue = QueueManager::instance();
 | 
			
		||||
  while (true) {
 | 
			
		||||
    std::vector<std::unique_ptr<InMessage>> messages =
 | 
			
		||||
        queue->for_querydb.DequeueAll();
 | 
			
		||||
    bool did_work = messages.size();
 | 
			
		||||
    for (auto& message : messages) {
 | 
			
		||||
      // TODO: Consider using std::unordered_map to lookup the handler
 | 
			
		||||
      for (MessageHandler* handler : *MessageHandler::message_handlers) {
 | 
			
		||||
        if (handler->GetMethodType() == message->GetMethodType()) {
 | 
			
		||||
          handler->Run(std::move(message));
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (message)
 | 
			
		||||
        LOG_S(ERROR) << "No handler for " << message->GetMethodType();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (int i = 80; i--;) {
 | 
			
		||||
      std::optional<Index_OnIndexed> response = queue->on_indexed.TryPopFront();
 | 
			
		||||
      if (!response)
 | 
			
		||||
        break;
 | 
			
		||||
      did_work = true;
 | 
			
		||||
      QueryDb_OnIndexed(queue, &db, &semantic_cache, &working_files, &*response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Cleanup and free any unused memory.
 | 
			
		||||
    FreeUnusedMemory();
 | 
			
		||||
 | 
			
		||||
    if (!did_work) {
 | 
			
		||||
      auto* queue = QueueManager::instance();
 | 
			
		||||
      querydb_waiter->Wait(&queue->on_indexed, &queue->for_querydb);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,30 +1,28 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "queue_manager.h"
 | 
			
		||||
#include "timer.h"
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
struct ClangTranslationUnit;
 | 
			
		||||
class DiagnosticsEngine;
 | 
			
		||||
struct VFS;
 | 
			
		||||
struct ICacheManager;
 | 
			
		||||
struct MultiQueueWaiter;
 | 
			
		||||
struct Project;
 | 
			
		||||
struct QueryDatabase;
 | 
			
		||||
struct SemanticHighlightSymbolCache;
 | 
			
		||||
struct WorkingFiles;
 | 
			
		||||
 | 
			
		||||
struct ImportPipelineStatus {
 | 
			
		||||
  std::atomic<int> num_active_threads = {0};
 | 
			
		||||
  std::atomic<long long> next_progress_output = {0};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void Indexer_Main(DiagnosticsEngine* diag_engine,
 | 
			
		||||
                  VFS* vfs,
 | 
			
		||||
                  ImportPipelineStatus* status,
 | 
			
		||||
                  Project* project,
 | 
			
		||||
                  WorkingFiles* working_files,
 | 
			
		||||
                  MultiQueueWaiter* waiter);
 | 
			
		||||
 | 
			
		||||
bool QueryDb_ImportMain(QueryDatabase* db,
 | 
			
		||||
                        ImportPipelineStatus* status,
 | 
			
		||||
                        SemanticHighlightSymbolCache* semantic_cache,
 | 
			
		||||
                        WorkingFiles* working_files);
 | 
			
		||||
void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times);
 | 
			
		||||
void LaunchStdoutThread(std::unordered_map<MethodType, Timer>* request_times,
 | 
			
		||||
                        MultiQueueWaiter* waiter);
 | 
			
		||||
void MainLoop(MultiQueueWaiter* querydb_waiter,
 | 
			
		||||
              MultiQueueWaiter* indexer_waiter);
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,6 @@
 | 
			
		||||
#include "lsp.h"
 | 
			
		||||
#include "maybe.h"
 | 
			
		||||
#include "nt_string.h"
 | 
			
		||||
#include "performance.h"
 | 
			
		||||
#include "position.h"
 | 
			
		||||
#include "serializer.h"
 | 
			
		||||
#include "symbol.h"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										135
									
								
								src/main.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/main.cc
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,135 @@
 | 
			
		||||
#include "import_pipeline.h"
 | 
			
		||||
#include "platform.h"
 | 
			
		||||
#include "serializer.h"
 | 
			
		||||
#include "serializers/json.h"
 | 
			
		||||
#include "test.h"
 | 
			
		||||
#include "working_files.h"
 | 
			
		||||
 | 
			
		||||
#include <llvm/Support/CommandLine.h>
 | 
			
		||||
#include <llvm/Support/Process.h>
 | 
			
		||||
using namespace llvm;
 | 
			
		||||
using namespace llvm::cl;
 | 
			
		||||
 | 
			
		||||
#include <doctest/doctest.h>
 | 
			
		||||
#include <rapidjson/error/en.h>
 | 
			
		||||
#include <loguru.hpp>
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
std::string g_init_options;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
opt<bool> opt_help("h", desc("Alias for -help"));
 | 
			
		||||
opt<int> opt_verbose("v", desc("verbosity"), init(0));
 | 
			
		||||
opt<bool> opt_test_index("test-index", desc("run index tests"));
 | 
			
		||||
opt<bool> opt_test_unit("test-unit", desc("run unit tests"));
 | 
			
		||||
 | 
			
		||||
opt<std::string> opt_init("init", desc("extra initialization options"));
 | 
			
		||||
opt<std::string> opt_log_file("log-file", desc("log"), value_desc("filename"));
 | 
			
		||||
opt<std::string> opt_log_file_append("log-file-append", desc("log"), value_desc("filename"));
 | 
			
		||||
 | 
			
		||||
list<std::string> opt_extra(Positional, ZeroOrMore, desc("extra"));
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv) {
 | 
			
		||||
  TraceMe();
 | 
			
		||||
 | 
			
		||||
  ParseCommandLineOptions(argc, argv,
 | 
			
		||||
                          "C/C++/Objective-C language server\n\n"
 | 
			
		||||
                          "See more on https://github.com/MaskRay/ccls/wiki");
 | 
			
		||||
 | 
			
		||||
  if (opt_help) {
 | 
			
		||||
    PrintHelpMessage();
 | 
			
		||||
    // Also emit doctest help if --test-unit is passed.
 | 
			
		||||
    if (!opt_test_unit)
 | 
			
		||||
      return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loguru::g_stderr_verbosity = opt_verbose - 1;
 | 
			
		||||
  loguru::g_preamble_date = false;
 | 
			
		||||
  loguru::g_preamble_time = false;
 | 
			
		||||
  loguru::g_preamble_verbose = false;
 | 
			
		||||
  loguru::g_flush_interval_ms = 0;
 | 
			
		||||
  loguru::init(argc, argv);
 | 
			
		||||
 | 
			
		||||
  MultiQueueWaiter querydb_waiter, indexer_waiter, stdout_waiter;
 | 
			
		||||
  QueueManager::Init(&querydb_waiter, &indexer_waiter, &stdout_waiter);
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
  // We need to write to stdout in binary mode because in Windows, writing
 | 
			
		||||
  // \n will implicitly write \r\n. Language server API will ignore a
 | 
			
		||||
  // \r\r\n split request.
 | 
			
		||||
  _setmode(_fileno(stdout), O_BINARY);
 | 
			
		||||
  _setmode(_fileno(stdin), O_BINARY);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  IndexInit();
 | 
			
		||||
 | 
			
		||||
  bool language_server = true;
 | 
			
		||||
 | 
			
		||||
  if (opt_log_file.size())
 | 
			
		||||
    loguru::add_file(opt_log_file.c_str(), loguru::Truncate, opt_verbose);
 | 
			
		||||
  if (opt_log_file_append.size())
 | 
			
		||||
    loguru::add_file(opt_log_file_append.c_str(), loguru::Append, opt_verbose);
 | 
			
		||||
 | 
			
		||||
  if (opt_test_unit) {
 | 
			
		||||
    language_server = false;
 | 
			
		||||
    doctest::Context context;
 | 
			
		||||
    std::vector<const char*> args{argv[0]};
 | 
			
		||||
    if (opt_help)
 | 
			
		||||
      args.push_back("-h");
 | 
			
		||||
    for (auto& arg : opt_extra)
 | 
			
		||||
      args.push_back(arg.c_str());
 | 
			
		||||
    context.applyCommandLine(args.size(), args.data());
 | 
			
		||||
    int res = context.run();
 | 
			
		||||
    if (res != 0 || context.shouldExit())
 | 
			
		||||
      return res;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (opt_test_index) {
 | 
			
		||||
    language_server = false;
 | 
			
		||||
    if (!RunIndexTests("", sys::Process::StandardInIsUserInput()))
 | 
			
		||||
      return 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (language_server) {
 | 
			
		||||
    if (!opt_init.empty()) {
 | 
			
		||||
      // We check syntax error here but override client-side
 | 
			
		||||
      // initializationOptions in messages/initialize.cc
 | 
			
		||||
      g_init_options = opt_init;
 | 
			
		||||
      rapidjson::Document reader;
 | 
			
		||||
      rapidjson::ParseResult ok = reader.Parse(g_init_options.c_str());
 | 
			
		||||
      if (!ok) {
 | 
			
		||||
        fprintf(stderr, "Failed to parse --init as JSON: %s (%zd)\n",
 | 
			
		||||
                rapidjson::GetParseError_En(ok.Code()), ok.Offset());
 | 
			
		||||
        return 1;
 | 
			
		||||
      }
 | 
			
		||||
      JsonReader json_reader{&reader};
 | 
			
		||||
      try {
 | 
			
		||||
        Config config;
 | 
			
		||||
        Reflect(json_reader, config);
 | 
			
		||||
      } catch (std::invalid_argument& e) {
 | 
			
		||||
        fprintf(stderr, "Failed to parse --init %s, expected %s\n",
 | 
			
		||||
                static_cast<JsonReader&>(json_reader).GetPath().c_str(),
 | 
			
		||||
                e.what());
 | 
			
		||||
        return 1;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::unordered_map<MethodType, Timer> request_times;
 | 
			
		||||
 | 
			
		||||
    // The thread that reads from stdin and dispatchs commands to the main thread.
 | 
			
		||||
    LaunchStdinLoop(&request_times);
 | 
			
		||||
    // The thread that writes responses from the main thread to stdout.
 | 
			
		||||
    LaunchStdoutThread(&request_times, &stdout_waiter);
 | 
			
		||||
    // Main thread which also spawns indexer threads upon the "initialize" request.
 | 
			
		||||
    MainLoop(&querydb_waiter, &indexer_waiter);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
@ -17,7 +17,6 @@ struct Config;
 | 
			
		||||
class DiagnosticsEngine;
 | 
			
		||||
struct VFS;
 | 
			
		||||
struct ImportManager;
 | 
			
		||||
struct ImportPipelineStatus;
 | 
			
		||||
struct IncludeComplete;
 | 
			
		||||
struct MultiQueueWaiter;
 | 
			
		||||
struct Project;
 | 
			
		||||
@ -108,7 +107,6 @@ struct MessageHandler {
 | 
			
		||||
  DiagnosticsEngine* diag_engine = nullptr;
 | 
			
		||||
  VFS* vfs = nullptr;
 | 
			
		||||
  ImportManager* import_manager = nullptr;
 | 
			
		||||
  ImportPipelineStatus* import_pipeline_status = nullptr;
 | 
			
		||||
  SemanticHighlightSymbolCache* semantic_cache = nullptr;
 | 
			
		||||
  WorkingFiles* working_files = nullptr;
 | 
			
		||||
  ClangCompleteManager* clang_complete = nullptr;
 | 
			
		||||
 | 
			
		||||
@ -519,8 +519,7 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
 | 
			
		||||
          g_thread_id = i + 1;
 | 
			
		||||
          std::string name = "indexer" + std::to_string(i);
 | 
			
		||||
          SetThreadName(name.c_str());
 | 
			
		||||
          Indexer_Main(diag_engine, vfs, import_pipeline_status, project,
 | 
			
		||||
                       working_files, waiter);
 | 
			
		||||
          Indexer_Main(diag_engine, vfs, project, working_files, waiter);
 | 
			
		||||
        }).detach();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,30 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "serializer.h"
 | 
			
		||||
 | 
			
		||||
// Contains timing information for the entire pipeline for importing a file
 | 
			
		||||
// into the querydb.
 | 
			
		||||
struct PerformanceImportFile {
 | 
			
		||||
  // All units are in microseconds.
 | 
			
		||||
 | 
			
		||||
  // [indexer] clang parsing the file
 | 
			
		||||
  uint64_t index_parse = 0;
 | 
			
		||||
  // [indexer] build the IndexFile object from clang parse
 | 
			
		||||
  uint64_t index_build = 0;
 | 
			
		||||
  // [indexer] save the IndexFile to disk
 | 
			
		||||
  uint64_t index_save_to_disk = 0;
 | 
			
		||||
  // [indexer] loading previously cached index
 | 
			
		||||
  uint64_t index_load_cached = 0;
 | 
			
		||||
  // [indexer] create delta IndexUpdate object
 | 
			
		||||
  uint64_t index_make_delta = 0;
 | 
			
		||||
  // [querydb] update WorkingFile indexed file state
 | 
			
		||||
  // uint64_t querydb_update_working_file = 0;
 | 
			
		||||
  // [querydb] apply IndexUpdate
 | 
			
		||||
  // uint64_t querydb_apply_index_update = 0;
 | 
			
		||||
};
 | 
			
		||||
MAKE_REFLECT_STRUCT(PerformanceImportFile,
 | 
			
		||||
                    index_parse,
 | 
			
		||||
                    index_build,
 | 
			
		||||
                    index_save_to_disk,
 | 
			
		||||
                    index_load_cached,
 | 
			
		||||
                    index_make_delta);
 | 
			
		||||
@ -6,9 +6,6 @@
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
void PlatformInit();
 | 
			
		||||
 | 
			
		||||
std::string GetExecutablePath();
 | 
			
		||||
std::string NormalizePath(const std::string& path);
 | 
			
		||||
 | 
			
		||||
void SetThreadName(const char* name);
 | 
			
		||||
 | 
			
		||||
@ -8,40 +8,22 @@
 | 
			
		||||
 | 
			
		||||
#include <loguru.hpp>
 | 
			
		||||
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#if defined(__FreeBSD__)
 | 
			
		||||
#include <pthread_np.h>
 | 
			
		||||
#include <sys/thr.h>
 | 
			
		||||
#elif defined(__OpenBSD__)
 | 
			
		||||
#include <pthread_np.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <semaphore.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>  // required for stat.h
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
 | 
			
		||||
#include <semaphore.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
 | 
			
		||||
#if defined(__FreeBSD__)
 | 
			
		||||
#include <sys/param.h>   // MAXPATHLEN
 | 
			
		||||
#include <sys/sysctl.h>  // sysctl
 | 
			
		||||
#elif defined(__linux__)
 | 
			
		||||
#ifdef __GLIBC__
 | 
			
		||||
#include <malloc.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -114,52 +96,13 @@ std::optional<std::string> RealPathNotExpandSymlink(std::string path) {
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
void PlatformInit() {}
 | 
			
		||||
 | 
			
		||||
#ifdef __APPLE__
 | 
			
		||||
extern "C" int _NSGetExecutablePath(char* buf, uint32_t* bufsize);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// See
 | 
			
		||||
// https://stackoverflow.com/questions/143174/how-do-i-get-the-directory-that-a-program-is-running-from
 | 
			
		||||
std::string GetExecutablePath() {
 | 
			
		||||
#ifdef __APPLE__
 | 
			
		||||
  uint32_t size = 0;
 | 
			
		||||
  _NSGetExecutablePath(nullptr, &size);
 | 
			
		||||
  char* buffer = new char[size];
 | 
			
		||||
  _NSGetExecutablePath(buffer, &size);
 | 
			
		||||
  char* resolved = realpath(buffer, nullptr);
 | 
			
		||||
  std::string result(resolved);
 | 
			
		||||
  delete[] buffer;
 | 
			
		||||
  free(resolved);
 | 
			
		||||
  return result;
 | 
			
		||||
#elif defined(__FreeBSD__)
 | 
			
		||||
  static const int name[] = {
 | 
			
		||||
      CTL_KERN,
 | 
			
		||||
      KERN_PROC,
 | 
			
		||||
      KERN_PROC_PATHNAME,
 | 
			
		||||
      -1,
 | 
			
		||||
  };
 | 
			
		||||
  char path[MAXPATHLEN];
 | 
			
		||||
  size_t len = sizeof(path);
 | 
			
		||||
  path[0] = '\0';
 | 
			
		||||
  (void)sysctl(name, 4, path, &len, NULL, 0);
 | 
			
		||||
  return std::string(path);
 | 
			
		||||
#else
 | 
			
		||||
  char buffer[PATH_MAX] = {0};
 | 
			
		||||
  if (-1 == readlink("/proc/self/exe", buffer, PATH_MAX))
 | 
			
		||||
    return "";
 | 
			
		||||
  return buffer;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string NormalizePath(const std::string& path) {
 | 
			
		||||
  std::optional<std::string> resolved = RealPathNotExpandSymlink(path);
 | 
			
		||||
  return resolved ? *resolved : path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FreeUnusedMemory() {
 | 
			
		||||
#if defined(__GLIBC__)
 | 
			
		||||
#ifdef __GLIBC__
 | 
			
		||||
  malloc_trim(0);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,8 +3,6 @@
 | 
			
		||||
 | 
			
		||||
#include "utils.h"
 | 
			
		||||
 | 
			
		||||
#include <loguru.hpp>
 | 
			
		||||
 | 
			
		||||
#include <Windows.h>
 | 
			
		||||
#include <direct.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
@ -17,22 +15,6 @@
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
void PlatformInit() {
 | 
			
		||||
  // We need to write to stdout in binary mode because in Windows, writing
 | 
			
		||||
  // \n will implicitly write \r\n. Language server API will ignore a
 | 
			
		||||
  // \r\r\n split request.
 | 
			
		||||
  _setmode(_fileno(stdout), O_BINARY);
 | 
			
		||||
  _setmode(_fileno(stdin), O_BINARY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See
 | 
			
		||||
// https://stackoverflow.com/questions/143174/how-do-i-get-the-directory-that-a-program-is-running-from
 | 
			
		||||
std::string GetExecutablePath() {
 | 
			
		||||
  char result[MAX_PATH] = {0};
 | 
			
		||||
  GetModuleFileName(NULL, result, MAX_PATH);
 | 
			
		||||
  return NormalizePath(result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string NormalizePath(const std::string& path) {
 | 
			
		||||
  DWORD retval = 0;
 | 
			
		||||
  TCHAR buffer[MAX_PATH] = TEXT("");
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "method.h"
 | 
			
		||||
#include "performance.h"
 | 
			
		||||
#include "query.h"
 | 
			
		||||
#include "threaded_queue.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								src/utils.cc
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								src/utils.cc
									
									
									
									
									
								
							@ -16,15 +16,6 @@
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
using namespace std::placeholders;
 | 
			
		||||
 | 
			
		||||
// DEFAULT_RESOURCE_DIRECTORY is passed with quotes for non-MSVC compilers, ie,
 | 
			
		||||
// foo vs "foo".
 | 
			
		||||
#if defined(_MSC_VER)
 | 
			
		||||
#define _STRINGIFY(x) #x
 | 
			
		||||
#define ENSURE_STRING_MACRO_ARGUMENT(x) _STRINGIFY(x)
 | 
			
		||||
#else
 | 
			
		||||
#define ENSURE_STRING_MACRO_ARGUMENT(x) x
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void TrimInPlace(std::string& s) {
 | 
			
		||||
  auto f = [](char c) { return !isspace(c); };
 | 
			
		||||
  s.erase(s.begin(), std::find_if(s.begin(), s.end(), f));
 | 
			
		||||
@ -137,8 +128,9 @@ std::optional<std::string> ReadContent(const std::string& filename) {
 | 
			
		||||
 | 
			
		||||
void WriteToFile(const std::string& filename, const std::string& content) {
 | 
			
		||||
  FILE* f = fopen(filename.c_str(), "wb");
 | 
			
		||||
  if (!f || fwrite(content.c_str(), content.size(), 1, f) != 1) {
 | 
			
		||||
    LOG_S(ERROR) << "Failed to write to " << filename << ' ' << strerror(errno);
 | 
			
		||||
  if (!f ||
 | 
			
		||||
      (content.size() && fwrite(content.c_str(), content.size(), 1, f) != 1)) {
 | 
			
		||||
    LOG_S(ERROR) << "failed to write to " << filename << ' ' << strerror(errno);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  fclose(f);
 | 
			
		||||
@ -154,25 +146,5 @@ std::optional<int64_t> LastWriteTime(const std::string& filename) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetDefaultResourceDirectory() {
 | 
			
		||||
  std::string result;
 | 
			
		||||
 | 
			
		||||
  std::string resource_directory =
 | 
			
		||||
      std::string(ENSURE_STRING_MACRO_ARGUMENT(DEFAULT_RESOURCE_DIRECTORY));
 | 
			
		||||
  // Remove double quoted resource dir if it was passed with quotes
 | 
			
		||||
  // by the build system.
 | 
			
		||||
  if (resource_directory.size() >= 2 && resource_directory[0] == '"' &&
 | 
			
		||||
      resource_directory[resource_directory.size() - 1] == '"') {
 | 
			
		||||
    resource_directory =
 | 
			
		||||
        resource_directory.substr(1, resource_directory.size() - 2);
 | 
			
		||||
  }
 | 
			
		||||
  if (resource_directory.compare(0, 2, "..") == 0) {
 | 
			
		||||
    std::string executable_path = GetExecutablePath();
 | 
			
		||||
    size_t pos = executable_path.find_last_of('/');
 | 
			
		||||
    result = executable_path.substr(0, pos + 1);
 | 
			
		||||
    result += resource_directory;
 | 
			
		||||
  } else {
 | 
			
		||||
    result = resource_directory;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return NormalizePath(result);
 | 
			
		||||
  return DEFAULT_RESOURCE_DIRECTORY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user