From fb7a2d7a13cc0c274dada6c3bb728055c6aa4527 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 30 Jan 2022 00:36:37 +0100 Subject: [PATCH] text-stats: Add JSON output mode in chrome://tracing format This allows the whole trace to be captured and then loaded into chrome://tracing or https://ui.perfetto.dev --- pandatool/src/text-stats/textMonitor.cxx | 57 +++++++++++++++++++++--- pandatool/src/text-stats/textMonitor.h | 5 ++- pandatool/src/text-stats/textStats.cxx | 19 +++++++- pandatool/src/text-stats/textStats.h | 1 + 4 files changed, 72 insertions(+), 10 deletions(-) diff --git a/pandatool/src/text-stats/textMonitor.cxx b/pandatool/src/text-stats/textMonitor.cxx index 2f4f458387..dd437d6106 100644 --- a/pandatool/src/text-stats/textMonitor.cxx +++ b/pandatool/src/text-stats/textMonitor.cxx @@ -22,9 +22,10 @@ * */ TextMonitor:: -TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data ) : PStatMonitor(server) { - _outStream = outStream; //[PECI] - _show_raw_data = show_raw_data; +TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data, bool json) : PStatMonitor(server) { + _outStream = outStream; //[PECI] + _show_raw_data = show_raw_data; + _json = json; } /** @@ -73,6 +74,30 @@ got_bad_version(int client_major, int client_minor, << server_major << "." << server_minor << ".\n"; } +/** + * Called whenever a new Thread definition is received from the client. + * Generally, the client will send all of its threads over shortly after + * connecting, but there's no guarantee that they will all be received before + * the first frames are received. The monitor should be prepared to accept + * new Thread definitions midstream. + */ +void TextMonitor:: +new_thread(int thread_index) { + if (_json) { + const PStatClientData *client_data = get_client_data(); + + int pid = get_client_pid(); + if (pid < 0) { + pid = _dummy_pid; + } + + (*_outStream) + << "{\"name\":\"thread_name\",\"ph\":\"M\",\"pid\":" << pid + << ",\"tid\":" << thread_index << ",\"args\":{\"name\":\"" + << client_data->get_thread_name(thread_index) << "\"}},\n"; + } +} + /** * Called as each frame's data is made available. There is no gurantee the * frames will arrive in order, or that all of them will arrive at all. The @@ -84,12 +109,29 @@ new_data(int thread_index, int frame_number) { PStatView &view = get_view(thread_index); const PStatThreadData *thread_data = view.get_thread_data(); - if (frame_number == thread_data->get_latest_frame_number()) { - view.set_to_frame(frame_number); + view.set_to_frame(frame_number); - if (view.all_collectors_known()) { - const PStatClientData *client_data = get_client_data(); + if (true) { + const PStatClientData *client_data = get_client_data(); + if (_json) { + int pid = get_client_pid(); + if (pid < 0) { + pid = _dummy_pid; + } + + const PStatFrameData &frame_data = thread_data->get_frame(frame_number); + int num_events = frame_data.get_num_events(); + for (int i = 0; i < num_events; ++i) { + int collector_index = frame_data.get_time_collector(i); + (*_outStream) + << "{\"name\":\"" << client_data->get_collector_fullname(collector_index) + << "\",\"ts\":" << (uint64_t)(frame_data.get_time(i) * 1000000) + << ",\"ph\":\"" << (frame_data.is_start(i) ? 'B' : 'E') << "\"" + << ",\"tid\":" << thread_index << ",\"pid\":" << pid << "},\n"; + } + } + else { (*_outStream) << "\rThread " << client_data->get_thread_name(thread_index) << " frame " << frame_number << ", " @@ -149,6 +191,7 @@ new_data(int thread_index, int frame_number) { void TextMonitor:: lost_connection() { nout << "Lost connection.\n"; + ++_dummy_pid; } /** diff --git a/pandatool/src/text-stats/textMonitor.h b/pandatool/src/text-stats/textMonitor.h index 4c88340e46..13cfa860c5 100644 --- a/pandatool/src/text-stats/textMonitor.h +++ b/pandatool/src/text-stats/textMonitor.h @@ -29,7 +29,7 @@ class TextStats; */ class TextMonitor : public PStatMonitor { public: - TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data); + TextMonitor(TextStats *server, std::ostream *outStream, bool show_raw_data, bool json = false); TextStats *get_server(); virtual std::string get_monitor_name(); @@ -37,6 +37,7 @@ public: virtual void got_hello(); virtual void got_bad_version(int client_major, int client_minor, int server_major, int server_minor); + virtual void new_thread(int thread_index); virtual void new_data(int thread_index, int frame_number); virtual void lost_connection(); virtual bool is_thread_safe(); @@ -47,6 +48,8 @@ public: private: std::ostream *_outStream; //[PECI] bool _show_raw_data; + bool _json; + int _dummy_pid = 0; }; #include "textMonitor.I" diff --git a/pandatool/src/text-stats/textStats.cxx b/pandatool/src/text-stats/textStats.cxx index 9f2929f440..0e677172aa 100644 --- a/pandatool/src/text-stats/textStats.cxx +++ b/pandatool/src/text-stats/textStats.cxx @@ -50,6 +50,11 @@ TextStats() { "time per collector.", &TextStats::dispatch_none, &_show_raw_data, nullptr); + add_option + ("j", "", 0, + "Output data in JSON format.", + &TextStats::dispatch_none, &_json, nullptr); + add_option ("o", "filename", 0, "Filename where to print. If not given then stderr is being used.", @@ -66,7 +71,7 @@ TextStats() { PStatMonitor *TextStats:: make_monitor() { - return new TextMonitor(this, _outFile, _show_raw_data); + return new TextMonitor(this, _outFile, _show_raw_data, _json); } @@ -87,13 +92,23 @@ run() { nout << "Listening for connections.\n"; if (_got_outputFileName) { - _outFile = new std::ofstream(_outputFileName.c_str(), std::ios::out); + _outFile = new std::ofstream(_outputFileName.c_str(), std::ios::out | std::ios::trunc); } else { _outFile = &(nout); } + if (_json) { + (*_outFile) << "[\n"; + } + main_loop(&user_interrupted); nout << "Exiting.\n"; + + if (_json) { + // Remove the last comma. + _outFile->seekp(-3, std::ios::cur); + (*_outFile) << "\n]\n"; + } } diff --git a/pandatool/src/text-stats/textStats.h b/pandatool/src/text-stats/textStats.h index b6853ecb46..99725f0e30 100644 --- a/pandatool/src/text-stats/textStats.h +++ b/pandatool/src/text-stats/textStats.h @@ -37,6 +37,7 @@ public: private: int _port; bool _show_raw_data; + bool _json = false; // [PECI] bool _got_outputFileName;