diff --git a/src/log.cc b/src/log.cc index 3e2b5592..7d60c299 100644 --- a/src/log.cc +++ b/src/log.cc @@ -29,6 +29,13 @@ static std::mutex mtx; FILE *file; Verbosity verbosity; +bool SetVerbosity(int value) { + verbosity = static_cast(value); + if (value > Verbosity_DEBUG || value < Verbosity_FATAL) + return false; + return true; +} + Message::Message(Verbosity verbosity, const char *file, int line) : verbosity_(verbosity) { using namespace llvm; @@ -60,6 +67,7 @@ Message::Message(Verbosity verbosity, const char *file, int line) case Verbosity_ERROR: stream_ << 'E'; break; case Verbosity_WARNING: stream_ << 'W'; break; case Verbosity_INFO: stream_ << 'I'; break; + case Verbosity_DEBUG: stream_ << 'D'; break; default: stream_ << "V(" << int(verbosity_) << ')'; } // clang-format on diff --git a/src/log.hh b/src/log.hh index 5d06d41a..227c9e69 100644 --- a/src/log.hh +++ b/src/log.hh @@ -15,8 +15,11 @@ enum Verbosity { Verbosity_ERROR = -2, Verbosity_WARNING = -1, Verbosity_INFO = 0, + Verbosity_DEBUG = 1, }; + extern Verbosity verbosity; +bool SetVerbosity(int value); struct Message { std::stringstream stream_; diff --git a/src/main.cc b/src/main.cc index 39c6457f..e06425d7 100644 --- a/src/main.cc +++ b/src/main.cc @@ -49,7 +49,7 @@ namespace { OptionCategory C("ccls options"); opt opt_help("h", desc("Alias for -help"), cat(C)); -opt opt_verbose("v", desc("verbosity"), init(0), cat(C)); +opt opt_verbose("v", desc("verbosity, 0 default. From 1 (debug) to -3 (critical)"), init(0), cat(C)); opt opt_test_index("test-index", ValueOptional, init("!"), desc("run index tests"), cat(C)); @@ -86,7 +86,11 @@ int main(int argc, char **argv) { PrintHelpMessage(); return 0; } - ccls::log::verbosity = ccls::log::Verbosity(opt_verbose.getValue()); + + if (!ccls::log::SetVerbosity(opt_verbose.getValue())) { + fprintf(stderr, "Unsupported verbosity level set\n"); + } + pipeline::Init(); const char *env = getenv("CCLS_CRASH_RECOVERY"); diff --git a/src/pipeline.cc b/src/pipeline.cc index b83db890..9e82143c 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -35,6 +35,7 @@ limitations under the License. #include #include +#include #include #include #ifndef _WIN32 @@ -413,6 +414,42 @@ void Quit(SemaManager &manager) { no_active_threads.wait(lock, [] { return !active_threads; }); } +struct HeadersData { + std::size_t content_length; +}; + +void ReadHeaders(bool* eof, HeadersData* headers) { + static const std::string_view kContentLength("Content-Length: "); + std::string buf; + while (true) { + int c = getchar(); + if (c == EOF) { + *eof = true; + return; + } + if (c == '\n') { + if (buf.empty()) { + if (headers->content_length == 0) + LOG_S(WARNING) << "Content-Length was not defined in header"; + return; + } + if (!buf.compare(0, kContentLength.size(), kContentLength)) { + std::from_chars_result result = std::from_chars( + buf.c_str() + kContentLength.size(), + buf.c_str() + buf.size(), + headers->content_length); + if (result.ec != std::errc() || headers->content_length == 0) { + LOG_S(WARNING) << "Content-Length parse error / invalid value"; + return; + } + } + buf.clear(); + } else if (c != '\r') { + buf.push_back(c); + } + } +} + } // namespace void ThreadEnter() { @@ -483,52 +520,53 @@ void LaunchStdin() { ThreadEnter(); std::thread([]() { set_thread_name("stdin"); + bool eof = false; std::string str; - const std::string_view kContentLength("Content-Length: "); - while (true) { - int len = 0; + while (!eof) { + HeadersData headers{}; + ReadHeaders(&eof, &headers); + if (eof) + return; + if (headers.content_length == 0) + continue; + str.clear(); - while (true) { + str.reserve(headers.content_length); + for (std::size_t i = 0; i < headers.content_length; ++i) { int c = getchar(); - if (c == EOF) + if (c == EOF) { + eof = true; return; - if (c == '\n') { - if (str.empty()) - break; - if (!str.compare(0, kContentLength.size(), kContentLength)) - len = atoi(str.c_str() + kContentLength.size()); - str.clear(); - } else if (c != '\r') { - str += c; } + str.push_back(c); } - str.resize(len); - for (int i = 0; i < len; ++i) { - int c = getchar(); - if (c == EOF) - return; - str[i] = c; - } - - auto message = std::make_unique(len); + auto message = std::make_unique(headers.content_length); std::copy(str.begin(), str.end(), message.get()); auto document = std::make_unique(); - document->Parse(message.get(), len); - assert(!document->HasParseError()); + document->Parse(message.get(), headers.content_length); + if (document->HasParseError()) { + LOG_S(WARNING) << "Json parse error"; + continue; + } JsonReader reader{document.get()}; if (!reader.m->HasMember("jsonrpc") || - std::string((*reader.m)["jsonrpc"].GetString()) != "2.0") - break; + std::string((*reader.m)["jsonrpc"].GetString()) != "2.0") { + LOG_S(WARNING) << "Bad jsonrpc member"; + continue; + } RequestId id; std::string method; ReflectMember(reader, "id", id); ReflectMember(reader, "method", method); - if (method.empty()) + if (method.empty()) { + LOG_S(WARNING) << "Empty method"; continue; + } bool should_exit = method == "exit"; // g_config is not available before "initialize". Use 0 in that case. + LOG_S(DEBUG) << "Received method: " << method; on_request->PushBack( {id, std::move(method), std::move(message), std::move(document), chrono::steady_clock::now() + @@ -738,6 +776,7 @@ void NotifyOrRequest(const char *method, bool request, JsonWriter writer(&w); fn(writer); w.EndObject(); + LOG_S(DEBUG) << "Notify/Request sent " << method; for_stdout->PushBack(output.GetString()); } @@ -765,6 +804,7 @@ static void Reply(RequestId id, const char *key, JsonWriter writer(&w); fn(writer); w.EndObject(); + LOG_S(DEBUG) << "Reply sent: " << id.value; for_stdout->PushBack(output.GetString()); }