diff --git a/src/command_line.cc b/src/command_line.cc index 6ca7236f..5c4850da 100644 --- a/src/command_line.cc +++ b/src/command_line.cc @@ -18,6 +18,7 @@ #include "query.h" #include "query_utils.h" #include "queue_manager.h" +#include "recorder.h" #include "semantic_highlight_symbol_cache.h" #include "serializer.h" #include "serializers/json.h" @@ -56,9 +57,6 @@ namespace { std::vector kEmptyArgs; -// If true stdout will be printed to stderr. -bool g_log_stdin_stdout_to_stderr = false; - // This function returns true if e2e timing should be displayed for the given // IpcId. bool ShouldDisplayIpcTiming(IpcId id) { @@ -96,8 +94,8 @@ Other command line options: --init Override client provided initialization options https://github.com/cquery-project/cquery/wiki/Initialization-options - --log-stdin-stdout-to-stderr - Print stdin (requests) and stdout (responses) to stderr + --record + Writes stdin to .in and stdout to .out --log-file Logging file for diagnostics --log-all-to-stderr Write all log messages to STDERR. --wait-for-input Wait for an '[Enter]' before exiting @@ -273,8 +271,7 @@ void LaunchStdinLoop(Config* config, while (true) { std::unique_ptr message; optional err = - MessageRegistry::instance()->ReadMessageFromStdin( - g_log_stdin_stdout_to_stderr, &message); + MessageRegistry::instance()->ReadMessageFromStdin(&message); // Message parsing can fail if we don't recognize the method. if (err) { @@ -381,10 +378,7 @@ void LaunchStdoutThread(std::unordered_map* request_times, std::string(IpcIdToString(message.id))); } - if (g_log_stdin_stdout_to_stderr) { - std::cerr << "[COUT] |" << message.content << "|\n"; - std::cerr.flush(); - } + RecordOutput(message.content); std::cout << message.content; std::cout.flush(); @@ -460,8 +454,8 @@ int main(int argc, char** argv) { loguru::Verbosity_MAX); } - if (HasOption(options, "--log-stdin-stdout-to-stderr")) - g_log_stdin_stdout_to_stderr = true; + if (HasOption(options, "--record")) + EnableRecording(options["--record"]); if (HasOption(options, "--clang-sanity-check")) { language_server = false; diff --git a/src/language_server_api.cc b/src/language_server_api.cc index d8889f24..88215d6f 100644 --- a/src/language_server_api.cc +++ b/src/language_server_api.cc @@ -1,5 +1,6 @@ #include "language_server_api.h" +#include "recorder.h" #include "serializers/json.h" #include @@ -65,6 +66,8 @@ optional ReadJsonRpcContentFrom( content += *c; } + RecordInput(content); + return content; } @@ -119,7 +122,6 @@ optional ReadCharFromStdinBlocking() { } optional MessageRegistry::ReadMessageFromStdin( - bool log_stdin_to_stderr, std::unique_ptr* message) { optional content = ReadJsonRpcContentFrom(&ReadCharFromStdinBlocking); @@ -128,14 +130,6 @@ optional MessageRegistry::ReadMessageFromStdin( exit(1); } - if (log_stdin_to_stderr) { - // TODO: This should go inside of ReadJsonRpcContentFrom since it does not - // print the header. - std::string printed = "[CIN] |" + *content + "|\n"; - std::cerr << printed; - std::cerr.flush(); - } - rapidjson::Document document; document.Parse(content->c_str(), content->length()); assert(!document.HasParseError()); diff --git a/src/language_server_api.h b/src/language_server_api.h index ba52bec8..95aca47d 100644 --- a/src/language_server_api.h +++ b/src/language_server_api.h @@ -43,7 +43,6 @@ struct MessageRegistry { std::unordered_map allocators; optional ReadMessageFromStdin( - bool log_stdin_to_stderr, std::unique_ptr* message); optional Parse(Reader& visitor, std::unique_ptr* message); diff --git a/src/recorder.cc b/src/recorder.cc new file mode 100644 index 00000000..6da3081e --- /dev/null +++ b/src/recorder.cc @@ -0,0 +1,56 @@ +#include "recorder.h" + +#include + +#include +#include + +namespace { +std::ofstream* g_file_in = nullptr; +std::ofstream* g_file_out = nullptr; +} // namespace + +void EnableRecording(std::string path) { + if (path.empty()) + path = "cquery"; + + // We can only call |EnableRecording| once. + assert(!g_file_in && !g_file_out); + + // Open the files. + g_file_in = new std::ofstream(path + ".in", + std::ios::out | std::ios::trunc | std::ios::binary); + g_file_out = new std::ofstream(path + ".out", + std::ios::out | std::ios::trunc | std::ios::binary); + + // Make sure we can write to the files. + bool bad = false; + if (!g_file_in->good()) { + LOG_S(ERROR) << "record: cannot write to " << path << ".in"; + bad = true; + } + if (!g_file_out->good()) { + LOG_S(ERROR) << "record: cannot write to " << path << ".out"; + bad = true; + } + if (bad) { + delete g_file_in; + delete g_file_out; + g_file_in = nullptr; + g_file_out = nullptr; + } +} + +void RecordInput(std::string_view content) { + if (!g_file_in) + return; + (*g_file_in) << "Content-Length: " << content.size() << "\r\n\r\n" << content; + (*g_file_in).flush(); +} + +void RecordOutput(std::string_view content) { + if (!g_file_out) + return; + (*g_file_out) << content; + (*g_file_out).flush(); +} \ No newline at end of file diff --git a/src/recorder.h b/src/recorder.h new file mode 100644 index 00000000..772841d3 --- /dev/null +++ b/src/recorder.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include + +void EnableRecording(std::string path); +void RecordInput(std::string_view content); +void RecordOutput(std::string_view content); \ No newline at end of file