Use llvm::cl and simplify main.cc import_pipeline.cc

This commit is contained in:
Fangrui Song 2018-05-13 09:52:19 -07:00
parent 4207c3ece1
commit c81ca26a2e
15 changed files with 345 additions and 578 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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
bool QueryDb_ImportMain(QueryDatabase* db,
ImportPipelineStatus* status,
SemanticHighlightSymbolCache* semantic_cache,
WorkingFiles* working_files) {
void LaunchStdinLoop(std::unordered_map<MethodType, Timer>* request_times) {
std::thread([request_times]() {
SetThreadName("stdin");
auto* queue = QueueManager::instance();
bool did_work = false;
while (true) {
std::unique_ptr<InMessage> message;
std::optional<std::string> err =
MessageRegistry::instance()->ReadMessageFromStdin(&message);
for (int i = 80; i--; ) {
// 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 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();
}
// 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, status, semantic_cache, working_files,
&*response);
QueryDb_OnIndexed(queue, &db, &semantic_cache, &working_files, &*response);
}
return did_work;
// Cleanup and free any unused memory.
FreeUnusedMemory();
if (!did_work) {
auto* queue = QueueManager::instance();
querydb_waiter->Wait(&queue->on_indexed, &queue->for_querydb);
}
}
}

View File

@ -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);

View File

@ -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
View 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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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);

View File

@ -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);

View File

@ -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
}

View File

@ -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("");

View File

@ -1,7 +1,6 @@
#pragma once
#include "method.h"
#include "performance.h"
#include "query.h"
#include "threaded_queue.h"

View File

@ -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;
}