Browse Source

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
rdb 3 years ago
parent
commit
fb7a2d7a13

+ 50 - 7
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 (true) {
+    const PStatClientData *client_data = get_client_data();
 
-    if (view.all_collectors_known()) {
-      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;
 }
 
 /**

+ 4 - 1
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"

+ 17 - 2
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";
+  }
 }
 
 

+ 1 - 0
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;